From b25fd9c83cb1ea4625e21452de15026269dee7b4 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Sun, 6 Jul 2025 14:34:49 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B0=9D=E8=AF=95=E5=B0=86=E8=8A=82=E7=82=B9?= =?UTF-8?q?=E6=B5=81=E5=AF=BC=E5=87=BA=E4=B8=BAc#=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FlowStartTool/FlowEnv.cs | 2 +- Library/Api/IDynamicContext.cs | 44 +- Library/Api/IFlowControl.cs | 24 +- Library/Api/IFlowEnvironment.cs | 9 +- Library/Enums/ConnectionInvokeType.cs | 2 +- Library/Extension/FlowModelExtension.cs | 129 +++- Library/FlowNode/DynamicContext.cs | 97 ++- Library/FlowNode/FlowCanvasDetails.cs | 9 +- Library/FlowNode/FlowResult.cs | 14 +- .../FlowNode/LightweightFlowEnvironment.cs | 625 ++++++++++++++++++ Library/FlowNode/ParameterDetails.cs | 110 ++- Library/Utils/UIContextOperation.cs | 3 +- NodeFlow/Env/FlowControl.cs | 58 +- NodeFlow/Env/FlowEdit.cs | 151 +++-- NodeFlow/Env/FlowEnvironment.cs | 24 +- NodeFlow/Env/LocalFlowEnvironment.cs | 125 +++- NodeFlow/FlowNodeExtension.cs | 29 + NodeFlow/Model/Node/NodeModelBaseFunc.cs | 2 +- NodeFlow/Model/Node/SingleConditionNode.cs | 12 +- NodeFlow/Model/Node/SingleExpOpNode.cs | 12 +- NodeFlow/Model/Node/SingleFlipflopNode.cs | 2 +- NodeFlow/Model/Node/SingleFlowCallNode.cs | 12 +- NodeFlow/Model/Node/SingleGlobalDataNode.cs | 8 +- NodeFlow/Model/Node/SingleScriptNode.cs | 13 +- NodeFlow/Model/Node/SingleUINode.cs | 6 +- .../ChangeNodeConnectionOperation.cs | 161 +++-- .../Operation/ChangeParameterOperation.cs | 13 +- .../Operation/ContainerPlaceNodeOperation.cs | 13 +- .../ContainerTakeOutNodeOperation.cs | 7 +- .../Model/Operation/CreateCanvasOperation.cs | 8 +- .../Model/Operation/CreateNodeOperation.cs | 8 +- NodeFlow/Model/Operation/OperationBase.cs | 28 +- .../Model/Operation/RemoveCanvasOperation.cs | 8 +- .../Model/Operation/RemoveNodeOperation.cs | 26 +- .../SetConnectPriorityInvokeOperation.cs | 6 +- .../Model/Operation/SetStartNodeOperation.cs | 8 +- NodeFlow/Serein.NodeFlow.csproj | 4 + NodeFlow/Services/FlowModelService.cs | 49 ++ NodeFlow/Services/FlowOperationService.cs | 8 +- NodeFlow/Services/FlowWorkManagement.cs | 12 +- Workbench/App.xaml.cs | 8 +- Workbench/Serein.WorkBench.csproj | 2 +- Workbench/ServiceCollectionExtensions.cs | 8 +- Workbench/Services/FlowEEForwardingService.cs | 77 ++- Workbench/Services/FlowProjectService.cs | 10 +- 45 files changed, 1625 insertions(+), 361 deletions(-) create mode 100644 Library/FlowNode/LightweightFlowEnvironment.cs diff --git a/FlowStartTool/FlowEnv.cs b/FlowStartTool/FlowEnv.cs index 9668d1d..0cfecba 100644 --- a/FlowStartTool/FlowEnv.cs +++ b/FlowStartTool/FlowEnv.cs @@ -20,7 +20,7 @@ namespace Serein.FlowStartTool SynchronizationContext? uiContext = SynchronizationContext.Current; // 在UI线程上获取UI线程上下文信息 var uIContextOperation = new UIContextOperation(uiContext); // 封装一个调用UI线程的工具类 flowEnvironment.SetUIContextOperation(uIContextOperation); - flowEnvironment.LoadProject(new FlowEnvInfo { Project = flowProjectData }, fileDataPath); // 加载项目 + flowEnvironment.LoadProject(fileDataPath); // 加载项目 flowEnvironment.Event.EnvOutput += (infoType, value) => { diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index 22d0344..ca3667f 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -37,22 +37,23 @@ namespace Serein.Library.Api /// Exception ExceptionOfRuning { get; set; } - /* /// - /// 忽略处理该节点流程 - /// - void IgnoreFlowHandle(NodeModelBase node); + /// - /// 获取此次流程处理状态 + /// 获取节点的运行时参数数据 /// - /// - /// - bool GetIgnodeFlowStateUpload(NodeModelBase node); + /// 节点 + /// 第几个参数 + /// 数据 + void SetParamsTempData(string nodeModel, int index, object data); + /// - /// 恢复流程处理状态 + /// 获取节点的运行时参数数据 /// - /// - /// - void RecoverIgnodeFlowStateUpload(NodeModelBase node);*/ + /// 节点 + /// 第几个参数 + /// 获取到的参数 + bool TryGetParamsTempData(string nodeModel, int index, out object data); + /// @@ -60,33 +61,33 @@ namespace Serein.Library.Api /// /// 当前节点 /// 运行时上一节点 - void SetPreviousNode(IFlowNode currentNodeModel, IFlowNode PreviousNode); + void SetPreviousNode(string currentNodeModel, string PreviousNode); /// /// 获取当前节点的运行时上一节点,用以流程中获取数据 /// /// /// - IFlowNode GetPreviousNode(IFlowNode currentNodeModel); + string GetPreviousNode(string currentNodeModel); /// /// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取 /// /// /// - FlowResult GetFlowData(IFlowNode nodeModel); + FlowResult GetFlowData(string nodeModel); /// /// 上一节点数据透传到下一节点 /// /// - FlowResult TransmissionData(IFlowNode nodeModel); + FlowResult TransmissionData(string nodeModel); /// /// 添加或更新当前节点的数据 /// /// /// - void AddOrUpdate(IFlowNode nodeModel, FlowResult flowData); + void AddOrUpdate(string nodeModel, FlowResult flowData); /// /// 重置流程状态(用于对象池回收) @@ -97,14 +98,5 @@ namespace Serein.Library.Api /// 用以提前结束当前上下文流程的运行 /// void Exit(); - - /*/// - /// 定时循环触发 - /// - /// - /// - /// - /// - // Task CreateTimingTask(Action callback, int time = 100, int count = -1);*/ } } diff --git a/Library/Api/IFlowControl.cs b/Library/Api/IFlowControl.cs index 36df475..53e0823 100644 --- a/Library/Api/IFlowControl.cs +++ b/Library/Api/IFlowControl.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System.Collections.Generic; +using System.Threading.Tasks; namespace Serein.Library.Api { @@ -62,6 +63,27 @@ namespace Serein.Library.Api /// 被触发的表达式 /// 中断类型。0主动监视,1表达式 void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type); + + + /// + /// 调用流程接口,将返回 FlowResult.Value。如果需要 FlowResult 对象,请使用该方法的泛型版本。 + /// + /// 流程接口节点Guid + /// 调用时入参参数 + /// + Task InvokeAsync(string apiGuid, Dictionary dict); + + + + /// + /// 调用流程接口,将返回 FlowResult.Value。如果需要 FlowResult 对象,请使用该方法的泛型版本。 + /// + /// 流程接口节点Guid + /// 调用时入参参数 + /// + Task InvokeAsync(string apiGuid, Dictionary dict); + + } diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 7962d87..23afb31 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -846,9 +846,14 @@ namespace Serein.Library.Api /// /// 加载项目文件 /// - /// 包含项目信息的远程环境 /// - void LoadProject(FlowEnvInfo flowEnvInfo, string filePath); + void LoadProject(string filePath); + + /// + /// 加载项目文件 + /// + /// + Task LoadProjetAsync(string filePath); /// /// 保存项目 diff --git a/Library/Enums/ConnectionInvokeType.cs b/Library/Enums/ConnectionInvokeType.cs index 568fe8d..9164d57 100644 --- a/Library/Enums/ConnectionInvokeType.cs +++ b/Library/Enums/ConnectionInvokeType.cs @@ -7,7 +7,7 @@ namespace Serein.Library /// /// 表示了两个节点之间的连接关系,同时表示节点运行完成后,所会执行的下一个节点类型。 - /// + /// diff --git a/Library/Extension/FlowModelExtension.cs b/Library/Extension/FlowModelExtension.cs index fa54879..70d066a 100644 --- a/Library/Extension/FlowModelExtension.cs +++ b/Library/Extension/FlowModelExtension.cs @@ -192,7 +192,6 @@ namespace Serein.Library } } - /// /// 开始执行 /// @@ -229,7 +228,7 @@ namespace Serein.Library } catch (Exception ex) { - flowResult = new FlowResult(currentNode, context); + flowResult = new FlowResult(currentNode.Guid, context); context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode.Guid}]异常:" + ex); context.NextOrientation = ConnectionInvokeType.IsError; context.ExceptionOfRuning = ex; @@ -237,7 +236,7 @@ namespace Serein.Library #endregion #region 执行完成时更新栈 - context.AddOrUpdate(currentNode, flowResult); // 上下文中更新数据 + context.AddOrUpdate(currentNode.Guid, flowResult); // 上下文中更新数据 // 首先将指定类别后继分支的所有节点逆序推入栈中 var nextNodes = currentNode.SuccessorNodes[context.NextOrientation]; @@ -247,7 +246,7 @@ namespace Serein.Library if (nextNodes[index].DebugSetting.IsEnable) { //if (!ignodeState) - context.SetPreviousNode(nextNodes[index], currentNode); + context.SetPreviousNode(nextNodes[index].Guid, currentNode.Guid); stack.Push(nextNodes[index]); } } @@ -259,7 +258,7 @@ namespace Serein.Library // 筛选出启用的节点的节点 if (upstreamNodes[index].DebugSetting.IsEnable) { - context.SetPreviousNode(upstreamNodes[index], currentNode); + context.SetPreviousNode(upstreamNodes[index].Guid, currentNode.Guid); stack.Push(upstreamNodes[index]); } } @@ -289,18 +288,88 @@ namespace Serein.Library await Task.Delay(1); #endif } - } + public static async Task GetParametersAsync(this IFlowNode nodeModel, IDynamicContext context, CancellationToken token) + { + var md = nodeModel.MethodDetails; + var pds = md.ParameterDetailss; + + if (pds.Length == 0) + return []; + + object[] args; + object[] paramsArgs = null; // 改为强类型数组 object[] + + int paramsArgIndex = md.ParamsArgIndex; + + if (paramsArgIndex >= 0) + { + // 可变参数数量 + int paramsLength = pds.Length - paramsArgIndex; + + // 用 object[] 表示可变参数数组(如果类型固定也可以用 int[] 等) + paramsArgs = new object[paramsLength]; + + // 方法参数中占位,最后一项是 object[] + args = new object[paramsArgIndex + 1]; + args[paramsArgIndex] = paramsArgs; + } + else + { + args = new object[pds.Length]; + } + + // 并发处理常规参数 + Task[] mainArgTasks = new Task[paramsArgIndex >= 0 ? paramsArgIndex : pds.Length]; + + for (int i = 0; i < mainArgTasks.Length; i++) + { + var pd = pds[i]; + mainArgTasks[i] = pd.ToMethodArgData(context); + } + + await Task.WhenAll(mainArgTasks); + + for (int i = 0; i < mainArgTasks.Length; i++) + { + args[i] = mainArgTasks[i].Result; + } + + // 并发处理 params 参数 + if (paramsArgs != null) + { + int paramsLength = paramsArgs.Length; + Task[] paramTasks = new Task[paramsLength]; + + for (int i = 0; i < paramsLength; i++) + { + var pd = pds[paramsArgIndex + i]; + paramTasks[i] = pd.ToMethodArgData(context); + } + + await Task.WhenAll(paramTasks); + + for (int i = 0; i < paramsLength; i++) + { + paramsArgs[i] = paramTasks[i].Result; + } + + args[args.Length - 1] = paramsArgs; + } + + return args; + } + /// /// 获取对应的参数数组 /// - public static async Task GetParametersAsync(this IFlowNode nodeModel, IDynamicContext context, CancellationToken token) + public static async Task GetParametersAsync2(this IFlowNode nodeModel, IDynamicContext context, CancellationToken token) { if (nodeModel.MethodDetails.ParameterDetailss.Length == 0) { - return Array.Empty(); // 无参数 + return []; // 无参数 } var md = nodeModel.MethodDetails; var pds = md.ParameterDetailss; @@ -347,6 +416,50 @@ namespace Serein.Library return args; } + + + /// + /// 视为流程接口调用 + /// + /// + /// + /// + public static async Task ApiInvokeAsync(this IFlowNode flowCallNode, Dictionary param) + { + var pds = flowCallNode.MethodDetails.ParameterDetailss; + if (param.Keys.Count != pds.Length) + { + throw new ArgumentNullException($"参数数量不一致。传入参数数量:{param.Keys.Count}。接口入参数量:{pds.Length}。"); + } + + var context = new DynamicContext(flowCallNode.Env); + for (int index = 0; index < pds.Length; index++) + { + ParameterDetails pd = pds[index]; + if (param.TryGetValue(pd.Name, out var value)) + { + context.SetParamsTempData(flowCallNode.Guid, index, value); // 设置入参参数 + } + } + var cts = new CancellationTokenSource(); + var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token); + cts?.Cancel(); + cts?.Dispose(); + context.Exit(); + if (flowResult.Value is TResult result) + { + return result; + } + else if (flowResult is FlowResult && flowResult is TResult result2) + { + return result2; + } + else + { + throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。"); + } + } + /// /// 检查监视表达式是否生效 /// diff --git a/Library/FlowNode/DynamicContext.cs b/Library/FlowNode/DynamicContext.cs index 24869c6..a274482 100644 --- a/Library/FlowNode/DynamicContext.cs +++ b/Library/FlowNode/DynamicContext.cs @@ -46,6 +46,26 @@ namespace Serein.Library /// public Exception ExceptionOfRuning { get; set; } + /// + /// 每个流程上下文分别存放节点的当前数据 + /// + private readonly ConcurrentDictionary dictNodeFlowData = new ConcurrentDictionary(); + + /// + /// 每个流程上下文存储运行时节点的调用关系 + /// + private readonly ConcurrentDictionary dictPreviousNodes = new ConcurrentDictionary(); + + /// + /// 记录忽略处理的流程 + /// + private readonly ConcurrentDictionary dictIgnoreNodeFlow = new ConcurrentDictionary(); + + /// + /// 记录节点的运行时参数数据 + /// + private readonly ConcurrentDictionary> dictNodeParams = new ConcurrentDictionary>(); + /* /// /// 每个流程上下文分别存放节点的当前数据 /// @@ -61,12 +81,70 @@ namespace Serein.Library /// private readonly ConcurrentDictionary dictIgnoreNodeFlow = new ConcurrentDictionary(); + /// + /// 记录节点的运行时参数数据 + /// + private readonly ConcurrentDictionary> dictNodeParams = new ConcurrentDictionary>();*/ + + /// + /// 设置节点的运行时参数数据 + /// + /// 节点 + /// 第几个参数 + /// 数据 + public void SetParamsTempData(string nodeModel, int index, object data) + { + if(!dictNodeParams.TryGetValue(nodeModel,out var dict)) + { + dict = new ConcurrentDictionary(); + dictNodeParams[nodeModel] = dict; + } + if (dict.TryGetValue(index, out var oldData)) + { + dict[index] = data; // 更新数据 + } + else + { + dict.TryAdd(index, data); // 添加新数据 + } + } + + /// + /// 获取节点的运行时参数数据 + /// + /// 节点 + /// 第几个参数 + public bool TryGetParamsTempData(string nodeModel, int index, out object data ) + { + if (dictNodeParams.TryGetValue(nodeModel, out var dict)) + { + if (dict.TryGetValue(index, out data)) + { + return true; // 返回数据 + } + else + { + //throw new KeyNotFoundException($"节点 {nodeModel.Guid} 的参数索引 {index} 不存在。"); + data = null; // 返回空数据 + return false; // 返回未找到 + } + } + else + { + //throw new KeyNotFoundException($"节点 {nodeModel.Guid} 的参数数据不存在。"); + data = null; // 返回空数据 + return false; // 返回未找到 + } + } + + + /// /// 设置运行时上一节点 /// /// 当前节点 /// 上一节点 - public void SetPreviousNode(IFlowNode currentNodeModel, IFlowNode PreviousNode) + public void SetPreviousNode(string currentNodeModel, string PreviousNode) { dictPreviousNodes.AddOrUpdate(currentNodeModel, (_) => PreviousNode, (o, n) => PreviousNode); } @@ -75,7 +153,7 @@ namespace Serein.Library /// 忽略处理该节点流程 /// /// - public void IgnoreFlowHandle(IFlowNode node) + public void IgnoreFlowHandle(string node) { dictIgnoreNodeFlow.AddOrUpdate(node, (o) => true, (o, n) => true); } @@ -85,7 +163,7 @@ namespace Serein.Library /// /// /// - public bool GetIgnodeFlowStateUpload(IFlowNode node) + public bool GetIgnodeFlowStateUpload(string node) { return dictIgnoreNodeFlow.TryGetValue(node, out var state) ? state : false; } @@ -94,7 +172,7 @@ namespace Serein.Library /// /// /// - public void RecoverIgnodeFlowStateUpload(IFlowNode node) + public void RecoverIgnodeFlowStateUpload(string node) { dictIgnoreNodeFlow.AddOrUpdate(node, (o) => false, (o, n) => false); } @@ -105,7 +183,7 @@ namespace Serein.Library /// /// /// - public IFlowNode GetPreviousNode(IFlowNode currentNodeModel) + public string GetPreviousNode(string currentNodeModel) { if (dictPreviousNodes.TryGetValue(currentNodeModel, out var node)) { @@ -122,7 +200,7 @@ namespace Serein.Library /// /// 节点 /// - public FlowResult GetFlowData(IFlowNode nodeGuid) + public FlowResult GetFlowData(string nodeGuid) { if (dictNodeFlowData.TryGetValue(nodeGuid, out var data)) { @@ -139,7 +217,7 @@ namespace Serein.Library /// /// 节点 /// 新的数据 - public void AddOrUpdate(IFlowNode nodeModel, FlowResult flowData) + public void AddOrUpdate(string nodeModel, FlowResult flowData) { // this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData); dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o,n ) => flowData); @@ -149,7 +227,7 @@ namespace Serein.Library /// 上一节点数据透传到下一节点 /// /// - public FlowResult TransmissionData(IFlowNode nodeModel) + public FlowResult TransmissionData(string nodeModel) { if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点 { @@ -159,7 +237,7 @@ namespace Serein.Library //AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中 } } - throw new InvalidOperationException($"透传{nodeModel.Guid}节点数据时发生异常:上一节点不存在数据"); + throw new InvalidOperationException($"透传{nodeModel}节点数据时发生异常:上一节点不存在数据"); } /// @@ -280,6 +358,7 @@ namespace Serein.Library list.Clear(); } + private void Dispose(ref IList list) { foreach (var nodeObj in list) diff --git a/Library/FlowNode/FlowCanvasDetails.cs b/Library/FlowNode/FlowCanvasDetails.cs index 79440a5..ae43fc9 100644 --- a/Library/FlowNode/FlowCanvasDetails.cs +++ b/Library/FlowNode/FlowCanvasDetails.cs @@ -29,14 +29,15 @@ namespace Serein.Library /// /// 画布拥有的节点 /// - [PropertyInfo(IsProtection = true)] - private System.Collections.ObjectModel.ObservableCollection _nodes = []; + [PropertyInfo(IsProtection = false)] + private List _nodes = []; + //private System.Collections.ObjectModel.ObservableCollection _nodes = []; /// /// 画布公开的节点 /// - [PropertyInfo(IsProtection = true)] - private System.Collections.ObjectModel.ObservableCollection _publicNodes = []; + [PropertyInfo(IsProtection = false)] + private List _publicNodes = []; /// /// 标识画布ID diff --git a/Library/FlowNode/FlowResult.cs b/Library/FlowNode/FlowResult.cs index 40fb360..cf54c26 100644 --- a/Library/FlowNode/FlowResult.cs +++ b/Library/FlowNode/FlowResult.cs @@ -28,11 +28,11 @@ namespace Serein.Library /// /// 实例化返回值 /// - /// + /// /// - public FlowResult(IFlowNode nodeModel, IDynamicContext context, object value) + public FlowResult(string nodeGuid, IDynamicContext context, object value) { - this.Source = nodeModel; + this.SourceNodeGuid = nodeGuid; this.ContextGuid = context.Guid; this.Value = value; } @@ -40,11 +40,11 @@ namespace Serein.Library /// /// 空返回值 /// - /// + /// /// - public FlowResult(IFlowNode nodeModel, IDynamicContext context) + public FlowResult(string nodeGuid, IDynamicContext context) { - this.Source = nodeModel; + this.SourceNodeGuid = nodeGuid; this.ContextGuid = context.Guid; this.Value = Unit.Default; } @@ -76,7 +76,7 @@ namespace Serein.Library /// /// 来源节点Guid /// - public IFlowNode Source{ get; } + public string SourceNodeGuid{ get; } /// /// 来源上下文Guid /// diff --git a/Library/FlowNode/LightweightFlowEnvironment.cs b/Library/FlowNode/LightweightFlowEnvironment.cs new file mode 100644 index 0000000..46c650c --- /dev/null +++ b/Library/FlowNode/LightweightFlowEnvironment.cs @@ -0,0 +1,625 @@ +using Microsoft.VisualBasic; +using Serein.Library.Api; +using Serein.Library.Utils; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Serein.Library.FlowNode +{ + + + /*public class FlowControl + { + private DynamicFlowTemplate dynamicFlowTemplate; + private LightweightEnvironment environment; + public FlowControl(DynamicFlowTemplate dynamicFlowTemplate, + LightweightEnvironment environment) + { + var callTree = new CallTree() + callTree.SetMainNode("node1_guid"); + + callTree.AddCallNode("node1_guid", (context) => DynamicFlowTemplate.Method(context, ...)); + callTree.AddCallNode("node2_guid", (context) => DynamicFlowTemplate.Method(context, ...)); + callTree.AddCallNode("node3_guid", (context) => DynamicFlowTemplate.Method(context, ...)); + callTree.AddCallNode("node4_guid", (context) => DynamicFlowTemplate.Method(context, ...)); + + callTree["node1_guid"].AddFlowUpstream("node2_guid"); + callTree["node1_guid"].AddFlowSucceed("node2_guid"); + callTree["node2_guid"].AddFlowSucceed("node3_guid"); + callTree["node2_guid"].AddFlowError("node4_guid"); + + LightweightEnvironment.LoadProject(callTree); + } + }*/ + + + /*public class DynamicFlowTemplate + { + private readonly class1_type class1_instance; + + public DynamicFlowTemplate(class1_type class_instance){ + this.class1_instance = class1_instance; + } + + [Description("node1_guid")] + [Description("assembly_name.class_name.method_name")] + private [methodReturnType / void] NodeMethod_[method_name](IDynamicContext context, type1 param1, type2 param2) + { + class1_instance.method(params); + context.SetData("node_guid", null); + } + + [Description("node2_guid")] + [Description("assembly_name.class_name.method_name")] + private [methodReturnType] NodeMethod_[method_name](IDynamicContext context, type1 param1) + { + param1 = context.GetData("node1_guid") as type1; + var result = class1_instance.method(params, param1); + context.SetData("node2_guid", result); + } + + + [Description("node3_guid")] + public [methodReturnType] FlowCall_[method_name](IDynamicContext context, type1 param1, type2 param2) + { + param1 = context.GetData("node3_guid") as type1; + var result = class1_instance.method(params, param1); + context.SetData("node3_guid", result); + } + } + */ + + + + + + + + + + + + public class CallTree + { + public CallTree(string canvasGuid, string startNodeGuid) + { + _canvas_guid = canvasGuid; + _start_node_guid = startNodeGuid; + } + + + private readonly ConcurrentDictionary _callNodes = new ConcurrentDictionary(); + private readonly string _start_node_guid; + private readonly string _canvas_guid ; + public CallNode this[string index] + { + get + { + _callNodes.TryGetValue(index, out CallNode callNode); + return callNode; + } + set + { + // 设置指定索引的值 + _callNodes.AddOrUpdate(index, value, (key, oldValue) => value); + } + } + + public void AddCallNode(string nodeGuid, Action action) + { + var node = new CallNode(this, nodeGuid, action); + _callNodes[nodeGuid] = node; + } + public void AddCallNode(string nodeGuid, Func func) + { + var node = new CallNode(this, nodeGuid, func); + _callNodes[nodeGuid] = node; + } + + } + + + + public class CallNode + { + + private readonly Func func; + private readonly CallTree callTree; + private readonly Action action; + + public CallNode(CallTree callTree, string nodeGuid, Action action) + { + this.callTree = callTree; + Guid = nodeGuid; + this.action = action; + } + + public CallNode(CallTree callTree, string nodeGuid, Func func) + { + Guid = nodeGuid; + this.func = func; + Init(); + } + + private void Init() + { + PreviousNodes = new Dictionary>(); + SuccessorNodes = new Dictionary>(); + foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) + { + PreviousNodes[ctType] = new List(); + SuccessorNodes[ctType] = new List(); + } + } + + + /// + /// 对应的节点 + /// + public string Guid { get; } + /// + /// 不同分支的父节点(流程调用) + /// + public Dictionary> PreviousNodes { get; private set; } + + /// + /// 不同分支的子节点(流程调用) + /// + public Dictionary> SuccessorNodes { get; private set; } + + + public CallNode AddChildNodeUpstream(string nodeGuid) + { + var connectionInvokeType = ConnectionInvokeType.Upstream; + PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]); + return this; + } + public CallNode AddChildNodeSucceed(string nodeGuid) + { + var connectionInvokeType = ConnectionInvokeType.IsSucceed; + PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]); + return this; + } + public CallNode AddChildNodeFail(string nodeGuid) + { + var connectionInvokeType = ConnectionInvokeType.IsFail; + PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]); + return this; + } + public CallNode AddChildNodeError(string nodeGuid) + { + var connectionInvokeType = ConnectionInvokeType.IsError; + PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]); + return this; + } + + + /// + /// 调用 + /// + /// + /// + /// + /// + public async Task InvokeAsync(IDynamicContext context, CancellationToken token) + { + if (token.IsCancellationRequested) + { + return; + } + if (action is not null) + { + action(context); + } + else if (func is not null) + { + await func(context); + } + else + { + throw new InvalidOperationException($"生成了错误的CallNode。【{Guid}】"); + } + } + + /// + /// 开始执行 + /// + /// + /// 流程运行 + /// + public async Task StartFlowAsync(IDynamicContext context, CancellationToken token) + { + Stack stack = new Stack(); + HashSet processedNodes = new HashSet(); // 用于记录已处理上游节点的节点 + stack.Push(this); + + while (true) + { + if (token.IsCancellationRequested) + { + throw new Exception($"流程执行被取消,未能获取到流程结果。"); + } + + #region 执行相关 + // 从栈中弹出一个节点作为当前节点进行处理 + var currentNode = stack.Pop(); + context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态 + FlowResult flowResult = null; + try + { + await currentNode.InvokeAsync(context, token); + + if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置 + { + context.NextOrientation = ConnectionInvokeType.IsSucceed; + } + } + catch (Exception ex) + { + flowResult = new FlowResult(currentNode.Guid, context); + context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode}]异常:" + ex); + context.NextOrientation = ConnectionInvokeType.IsError; + context.ExceptionOfRuning = ex; + } + #endregion + + #region 执行完成时更新栈 + context.AddOrUpdate(currentNode.Guid, flowResult); // 上下文中更新数据 + + // 首先将指定类别后继分支的所有节点逆序推入栈中 + var nextNodes = currentNode.SuccessorNodes[context.NextOrientation]; + for (int index = nextNodes.Count - 1; index >= 0; index--) + { + //if (!ignodeState) + context.SetPreviousNode(nextNodes[index].Guid, currentNode.Guid); + stack.Push(nextNodes[index]); + } + + // 然后将指上游分支的所有节点逆序推入栈中 + var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream]; + for (int index = upstreamNodes.Count - 1; index >= 0; index--) + { + context.SetPreviousNode(upstreamNodes[index].Guid, currentNode.Guid); + stack.Push(upstreamNodes[index]); + } + #endregion + + #region 执行完成后检查 + + if (stack.Count == 0) + { + return flowResult; // 说明流程到了终点 + } + + if (context.RunState == RunState.Completion) + { + context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。"); + return flowResult; // 流程执行完成,返回结果 + } + + if (token.IsCancellationRequested) + { + throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。"); + } + + + #endregion + } + } + } + + /// + /// 轻量级流程控制器 + /// + public class LightweightFlowControl : IFlowControl + { + private readonly Dictionary callTree = new Dictionary(); + + public LightweightFlowControl() + { + } + + public Task InvokeAsync(string apiGuid, Dictionary dict) + { + throw new NotImplementedException(); + } + + public Task InvokeAsync(string apiGuid, Dictionary dict) + { + throw new NotImplementedException(); + } + + public Task StartFlowFromSelectNodeAsync(string startNodeGuid) + { + throw new NotImplementedException(); + } + public async Task StartFlowAsync(string[] canvasGuids) + { + throw new NotImplementedException(); + } + + public Task ExitFlowAsync() + { + throw new NotImplementedException(); + } + + #region 无须实现 + + public void ActivateFlipflopNode(string nodeGuid) + { + throw new NotImplementedException(); + } + + public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) + { + throw new NotImplementedException(); + } + + + + public void TerminateFlipflopNode(string nodeGuid) + { + throw new NotImplementedException(); + } + + public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) + { + throw new NotImplementedException(); + } + + public void UseExternalIOC(ISereinIOC ioc) + { + throw new NotImplementedException(); + } + #endregion + } + + + /// + /// 轻量级流程环境事件实现 + /// + public class LightweightFlowEnvironmentEvent : IFlowEnvironmentEvent + { + public event LoadDllHandler DllLoad; + public event ProjectLoadedHandler ProjectLoaded; + public event ProjectSavingHandler ProjectSaving; + public event NodeConnectChangeHandler NodeConnectChanged; + public event CanvasCreateHandler CanvasCreated; + public event CanvasRemoveHandler CanvasRemoved; + public event NodeCreateHandler NodeCreated; + public event NodeRemoveHandler NodeRemoved; + public event NodePlaceHandler NodePlace; + public event NodeTakeOutHandler NodeTakeOut; + public event StartNodeChangeHandler StartNodeChanged; + public event FlowRunCompleteHandler FlowRunComplete; + public event MonitorObjectChangeHandler MonitorObjectChanged; + public event NodeInterruptStateChangeHandler NodeInterruptStateChanged; + public event ExpInterruptTriggerHandler InterruptTriggered; + public event IOCMembersChangedHandler IOCMembersChanged; + public event NodeLocatedHandler NodeLocated; + public event EnvOutHandler EnvOutput; + + public void OnDllLoad(LoadDllEventArgs eventArgs) + { + DllLoad?.Invoke(eventArgs); + } + + public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs) + { + ProjectLoaded?.Invoke(eventArgs); + } + + public void OnProjectSaving(ProjectSavingEventArgs eventArgs) + { + ProjectSaving?.Invoke(eventArgs); + } + + public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs) + { + NodeConnectChanged?.Invoke(eventArgs); + } + + public void OnCanvasCreated(CanvasCreateEventArgs eventArgs) + { + CanvasCreated?.Invoke(eventArgs); + } + + public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs) + { + CanvasRemoved?.Invoke(eventArgs); + } + + public void OnNodeCreated(NodeCreateEventArgs eventArgs) + { + NodeCreated?.Invoke(eventArgs); + } + + public void OnNodeRemoved(NodeRemoveEventArgs eventArgs) + { + NodeRemoved?.Invoke(eventArgs); + } + + public void OnNodePlace(NodePlaceEventArgs eventArgs) + { + NodePlace?.Invoke(eventArgs); + } + + public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs) + { + NodeTakeOut?.Invoke(eventArgs); + } + + public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs) + { + StartNodeChanged?.Invoke(eventArgs); + } + + public void OnFlowRunComplete(FlowEventArgs eventArgs) + { + FlowRunComplete?.Invoke(eventArgs); + } + + public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs) + { + MonitorObjectChanged?.Invoke(eventArgs); + } + + public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs) + { + NodeInterruptStateChanged?.Invoke(eventArgs); + } + + public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs) + { + InterruptTriggered?.Invoke(eventArgs); + } + + public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs) + { + IOCMembersChanged?.Invoke(eventArgs); + } + + public void OnNodeLocated(NodeLocatedEventArgs eventArgs) + { + NodeLocated?.Invoke(eventArgs); + } + + public void OnEnvOutput(InfoType type, string value) + { + EnvOutput?.Invoke(type, value); + } + + } + + /// + /// 轻量级流程环境实现 + /// + public class LightweightFlowEnvironment : IFlowEnvironment + { + public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial) + { + Console.WriteLine(message); + } + + + public ISereinIOC IOC => throw new NotImplementedException(); + + public IFlowEdit FlowEdit => throw new NotImplementedException(); + + public IFlowControl FlowControl => throw new NotImplementedException(); + + public IFlowEnvironmentEvent Event => throw new NotImplementedException(); + + public string EnvName => throw new NotImplementedException(); + + public string ProjectFileLocation => throw new NotImplementedException(); + + public bool IsGlobalInterrupt => throw new NotImplementedException(); + + public bool IsControlRemoteEnv => throw new NotImplementedException(); + + public InfoClass InfoClass { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public IFlowEnvironment CurrentEnv => throw new NotImplementedException(); + + public UIContextOperation UIContextOperation => throw new NotImplementedException(); + + public Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) + { + throw new NotImplementedException(); + } + + public void ExitRemoteEnv() + { + throw new NotImplementedException(); + } + + public Task GetEnvInfoAsync() + { + throw new NotImplementedException(); + } + + public Task GetProjectInfoAsync() + { + throw new NotImplementedException(); + } + + public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) + { + throw new NotImplementedException(); + } + + public void LoadLibrary(string dllPath) + { + throw new NotImplementedException(); + } + + public bool LoadNativeLibraryOfRuning(string file) + { + throw new NotImplementedException(); + } + + public void LoadProject(string filePath) + { + throw new NotImplementedException(); + } + + public Task LoadProjetAsync(string filePath) + { + throw new NotImplementedException(); + } + + public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) + { + throw new NotImplementedException(); + } + + public void SaveProject() + { + throw new NotImplementedException(); + } + + public void SetUIContextOperation(UIContextOperation uiContextOperation) + { + throw new NotImplementedException(); + } + + public Task StartRemoteServerAsync(int port = 7525) + { + throw new NotImplementedException(); + } + + public void StopRemoteServer() + { + throw new NotImplementedException(); + } + + public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del) + { + throw new NotImplementedException(); + } + + public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo) + { + throw new NotImplementedException(); + } + + public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel) + { + throw new NotImplementedException(); + } + + public bool TryUnloadLibrary(string assemblyFullName) + { + throw new NotImplementedException(); + } + + + } +} diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index b7c732b..267817b 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -208,14 +208,106 @@ namespace Serein.Library return pd; } + public async Task ToMethodArgData(IDynamicContext context) + { + // 1. 从缓存获取 + if (context.TryGetParamsTempData(NodeModel.Guid, Index, out var data)) + return data; + + // 2. 特定快捷类型 + if (typeof(IFlowEnvironment).IsAssignableFrom(DataType)) return NodeModel.Env; + if (typeof(IDynamicContext).IsAssignableFrom(DataType)) return context; + if (typeof(IFlowNode).IsAssignableFrom(DataType)) return NodeModel; + + // 3. 显式常量参数 + if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase)) + return DataValue.ToConvert(DataType); + + /* // 4. 枚举绑定类型 + if (ExplicitType is not null && ExplicitType.IsEnum && DataType != ExplicitType) + { + var resultEnum = Enum.Parse(ExplicitType, DataValue); + var boundType = EnumHelper.GetBoundValue(ExplicitType, resultEnum, attr => attr.Value) as Type; + if (boundType != null) + return NodeModel.Env.IOC.CreateObject(boundType); + }*/ + + // 5. 来自其他节点 + object inputParameter = null; + var env = NodeModel.Env; + + if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) + { + var prevNode = context.GetPreviousNode(NodeModel.Guid); + inputParameter = prevNode != null ? context.GetFlowData(prevNode)?.Value : null; + } + else + { + + var prevNodeGuid = context.GetPreviousNode(NodeModel.Guid); + + if (!env.TryGetNodeModel(ArgDataSourceNodeGuid, out var sourceNode)) + throw new Exception($"[arg{Index}] 节点[{ArgDataSourceNodeGuid}]不存在"); + + if (sourceNode.IsPublic + && env.TryGetNodeModel(prevNodeGuid, out var prevNode) + && prevNode.ControlType == NodeControlType.FlowCall + && env.TryGetNodeModel(context.GetPreviousNode(NodeModel.Guid), out var sourceNodeTemp) + ) + sourceNode = sourceNodeTemp; + + if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) + inputParameter = context.GetFlowData(sourceNode.Guid)?.Value; + else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) + inputParameter = (await sourceNode.ExecutingAsync(context, CancellationToken.None)).Value; + else + throw new Exception("无效的 ArgDataSourceType"); + } + + // 6. 表达式处理 + if (IsExplicitData && DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase)) + { + var lower = DataValue.ToLowerInvariant(); + if (lower.StartsWith("@get") || lower.StartsWith("@dtc") || lower.StartsWith("@data")) + { + inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _); + } + } + + // 7. 类型转换 + if (!DataType.IsValueType && inputParameter is null) + throw new Exception($"[arg{Index}] 参数不能为null"); + + if (DataType == typeof(string)) + return inputParameter.ToString(); + + var actualType = inputParameter.GetType(); + if (DataType.IsAssignableFrom(actualType)) + return inputParameter; + + if (DataType.IsSubclassOf(actualType)) + return ObjectConvertHelper.ConvertParentToChild(inputParameter, DataType); + + throw new Exception($"[arg{Index}] 类型不匹配:目标类型为 {DataType},实际类型为 {actualType}"); + } + /// /// 转为方法入参数据 /// /// - public async ValueTask ToMethodArgData(IDynamicContext context) + public async Task ToMethodArgData2(IDynamicContext context) { + var nodeModel = NodeModel; var env = nodeModel.Env; + + #region 流程运行上下文预设的参数 + if (context.TryGetParamsTempData(NodeModel.Guid, Index, out var data)) + { + return data; + } + #endregion + #region 显然的流程基本类型 // 返回运行环境 if (typeof(IFlowEnvironment).IsAssignableFrom(DataType)) @@ -236,8 +328,13 @@ namespace Serein.Library if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase)) { return DataValue.ToConvert(DataType); // 并非表达式,同时是显式设置的参数 - } + } #endregion + + + + + #region “枚举-类型”转换器 if (ExplicitType is not null && ExplicitType.IsEnum && DataType != ExplicitType) { @@ -258,7 +355,7 @@ namespace Serein.Library if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) { - var previousNode = context.GetPreviousNode(nodeModel); + var previousNode = context.GetPreviousNode(nodeModel.Guid); if (previousNode is null) { inputParameter = null; @@ -281,8 +378,9 @@ namespace Serein.Library // 如果是公开的节点,需要判断上下文调用中是否存在流程接口节点 if (argSourceNodeModel.IsPublic) { - var pn = context.GetPreviousNode(NodeModel); - if(pn.ControlType == NodeControlType.FlowCall) + var pnGuid = context.GetPreviousNode(NodeModel.Guid); + var pn = env.TryGetNodeModel(pnGuid, out var tmpNode) ? tmpNode : null; + if (pn.ControlType == NodeControlType.FlowCall) { argSourceNodeModel = pn; } @@ -290,7 +388,7 @@ namespace Serein.Library if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { - var flowData = context.GetFlowData(argSourceNodeModel); + var flowData = context.GetFlowData(argSourceNodeModel.Guid); if(flowData is null) { inputParameter = null; diff --git a/Library/Utils/UIContextOperation.cs b/Library/Utils/UIContextOperation.cs index be0355a..25a204d 100644 --- a/Library/Utils/UIContextOperation.cs +++ b/Library/Utils/UIContextOperation.cs @@ -72,7 +72,8 @@ namespace Serein.Library.Utils } catch (Exception ex) { - if(onException != null) onException(ex); + if(onException != null) + onException(ex); Debug.WriteLine(ex); } }, null); diff --git a/NodeFlow/Env/FlowControl.cs b/NodeFlow/Env/FlowControl.cs index 4fbfe58..e192815 100644 --- a/NodeFlow/Env/FlowControl.cs +++ b/NodeFlow/Env/FlowControl.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; -using static Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser; namespace Serein.NodeFlow.Env { @@ -25,11 +24,11 @@ namespace Serein.NodeFlow.Env private readonly UIContextOperation UIContextOperation; public FlowControl(IFlowEnvironment flowEnvironment, - IFlowEnvironmentEvent flowEnvironmentEvent, - FlowLibraryService flowLibraryService, - FlowOperationService flowOperationService, - FlowModelService flowModelService, - UIContextOperation UIContextOperation) + IFlowEnvironmentEvent flowEnvironmentEvent, + FlowLibraryService flowLibraryService, + FlowOperationService flowOperationService, + FlowModelService flowModelService, + UIContextOperation UIContextOperation) { this.flowEnvironment = flowEnvironment; this.flowEnvironmentEvent = flowEnvironmentEvent; @@ -285,38 +284,24 @@ namespace Serein.NodeFlow.Env /// 调用流程接口,将返回 FlowResult.Value。如果需要 FlowResult 对象,请使用该方法的泛型版本。 /// /// 流程接口节点Guid - /// 调用时入参参数 + /// 调用时入参参数 /// - /// - public async Task ApiInvokeAsync(string apiGuid, object[] param) + public async Task InvokeAsync(string apiGuid, Dictionary dict) { - if (sereinIOC is null) - { - sereinIOC = flowEnvironment.IOC; - } - if (!flowModelService.TryGetNodeModel(apiGuid, out var nodeModel)) - { - throw new ArgumentNullException($"不存在流程接口:{apiGuid}"); - } - if (nodeModel is not SingleFlowCallNode flowCallNode) - { - throw new ArgumentNullException($"目标节点并非流程接口:{apiGuid}"); - } - var context = contexts.Allocate(); - CancellationTokenSource cts = new CancellationTokenSource(); - var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token); - return flowResult.Value; + var result = await InvokeAsync(apiGuid, dict); + return result; } + /// /// 调用流程接口,泛型类型为 FlowResult 时,将返回 FlowResult 对象。 /// /// /// 流程接口节点Guid - /// 调用时入参参数 + /// 调用时入参参数 /// /// - public async Task ApiInvokeAsync(string apiGuid, object[] param) + public async Task InvokeAsync(string apiGuid, Dictionary dict) { if (sereinIOC is null) { @@ -330,10 +315,25 @@ namespace Serein.NodeFlow.Env { throw new ArgumentNullException($"目标节点并非流程接口:{apiGuid}"); } - var context = contexts.Allocate(); + var pds = flowCallNode.MethodDetails.ParameterDetailss; + if (dict.Keys.Count != pds.Length) + { + throw new ArgumentNullException($"参数数量不一致。传入参数数量:{dict.Keys.Count}。接口入参数量:{pds.Length}。"); + } + + IDynamicContext context = contexts.Allocate(); + for (int index = 0; index < pds.Length; index++) + { + ParameterDetails pd = pds[index]; + if (dict.TryGetValue(pd.Name, out var value)) + { + context.SetParamsTempData(flowCallNode.Guid, index, value); // 设置入参参数 + } + } CancellationTokenSource cts = new CancellationTokenSource(); var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token); - + cts?.Cancel(); + cts?.Dispose(); if (flowResult.Value is TResult result) { return result; diff --git a/NodeFlow/Env/FlowEdit.cs b/NodeFlow/Env/FlowEdit.cs index cb6d018..4ed8eb3 100644 --- a/NodeFlow/Env/FlowEdit.cs +++ b/NodeFlow/Env/FlowEdit.cs @@ -1,11 +1,15 @@ -using Serein.Library; +using Microsoft.CodeAnalysis; +using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; using Serein.NodeFlow.Model; using Serein.NodeFlow.Model.Operation; using Serein.NodeFlow.Services; using Serein.NodeFlow.Tool; +using System.Diagnostics; +using System.Threading.Tasks; using static Serein.Library.Api.IFlowEnvironment; +using IOperation = Serein.NodeFlow.Model.Operation.IOperation; namespace Serein.NodeFlow.Env { @@ -107,11 +111,13 @@ namespace Serein.NodeFlow.Env /// 从节点信息创建节点,并返回状态指示是否创建成功 /// /// + /// /// - private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo) + private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo, out IFlowNode? nodeModel) { if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) { + nodeModel = null; return false; } @@ -146,37 +152,22 @@ namespace Serein.NodeFlow.Env } else { - if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false; + if (string.IsNullOrEmpty(nodeInfo.MethodName)) + { + nodeModel = null; + return false; + } // 加载方法节点 flowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 } #endregion - var nodeModel = FlowNodeExtension.CreateNode(flowEnvironment, controlType, methodDetails); // 加载项目时创建节点 + nodeModel = FlowNodeExtension.CreateNode(flowEnvironment, controlType, methodDetails); // 加载项目时创建节点 if (nodeModel is null) { nodeInfo.Guid = string.Empty; return false; } - if (TryGetCanvasModel(nodeInfo.CanvasGuid, out var canvasModel)) - { - - // 节点与画布互相绑定 - // 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常 - nodeModel.CanvasDetails = canvasModel; - UIContextOperation?.Invoke(() => canvasModel.Nodes.Add(nodeModel)); - - nodeModel.LoadInfo(nodeInfo); // 创建节点model - TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 - } - else - { - SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在"); - return false; - } - - UIContextOperation?.Invoke(() => - flowEnvironmentEvent.OnNodeCreated(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 return true; } @@ -355,7 +346,10 @@ namespace Serein.NodeFlow.Env }*/ canvasModel.StartNode = newStartNodeModel; //newStartNode.IsStart = true; - UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnStartNodeChanged(new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, newStartNodeModel.Guid))); + _ = TriggerEvent(() => + flowEnvironmentEvent.OnStartNodeChanged( + new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, newStartNodeModel.Guid) + )); return; } @@ -395,6 +389,46 @@ namespace Serein.NodeFlow.Env #region 从NodeInfo创建NodeModel // 流程接口节点最后才创建 + async Task AddNodeAsync(NodeInfo nodeInfo, IFlowNode nodeModel) + { + if (!TryGetCanvasModel(nodeInfo.CanvasGuid, out var canvasModel)) + { + SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在"); + } + else + { + // 节点与画布互相绑定 + // 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常 + nodeModel.CanvasDetails = canvasModel; + await TriggerEvent(() => + { + try + { + var nodes = canvasModel.Nodes.ToList(); + nodes.Add(nodeModel); + canvasModel.Nodes = nodes; + } + catch (Exception ex) + { + Debug.WriteLine(ex.Message); + } + }); // 添加到画布节点集合中 + nodeModel.LoadInfo(nodeInfo); // 创建节点model + nodeModel.Guid ??= Guid.NewGuid().ToString(); + flowModelService.AddNodeModel(nodeModel); + + await TriggerEvent(() => + flowEnvironmentEvent.OnNodeCreated( + new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position) + ) + ); // 创建节点事件 + + } + + + } + + List flowCallNodeInfos = []; foreach (NodeInfo? nodeInfo in nodeInfos) { @@ -404,10 +438,13 @@ namespace Serein.NodeFlow.Env } else { - if (!CreateNodeFromNodeInfo(nodeInfo)) + if (CreateNodeFromNodeInfo(nodeInfo, out var nodeModel) && nodeModel is not null) + { + await AddNodeAsync(nodeInfo, nodeModel); + } + else { SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}"); - continue; } } } @@ -415,10 +452,13 @@ namespace Serein.NodeFlow.Env // 创建流程接口节点 foreach (NodeInfo? nodeInfo in flowCallNodeInfos) { - if (!CreateNodeFromNodeInfo(nodeInfo)) + if (CreateNodeFromNodeInfo(nodeInfo, out var nodeModel) && nodeModel is not null) + { + await AddNodeAsync(nodeInfo, nodeModel); + } + else { SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}"); - continue; } } #endregion @@ -444,8 +484,10 @@ namespace Serein.NodeFlow.Env var result = nodeContainer.PlaceNode(nodeModel); if (result) { - UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnNodePlace( - new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid))); + await TriggerEvent(() => + flowEnvironmentEvent.OnNodePlace( + new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid) + )); } @@ -484,30 +526,9 @@ namespace Serein.NodeFlow.Env ConnectInvokeNode(canvasGuid, fromNodeModel.Guid, toNodeModel.Guid, JunctionType.NextStep, JunctionType.Execute, item.connectionType); - //var isSuccessful = ConnectInvokeOfNode(canvasGuid, fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系 } } - - //List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), - // (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), - // (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), - // (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; - - //List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0) - // .Select(info => (info.connectionType, - // info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid]) - // .ToArray())) - // .ToList(); - // 遍历每种类型的节点分支(四种) - //foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in nodeInfo) - //{ - // // 遍历当前类型分支的节点(确认连接关系) - // foreach (var toNode in item.toNodes) - // { - // _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 - // } - //} } #endregion @@ -526,20 +547,13 @@ namespace Serein.NodeFlow.Env if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) && TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var fromNode)) { - ConnectArgSourceNode(canvasGuid, fromNode.Guid, toNode.Guid, JunctionType.ReturnData, JunctionType.ArgData, pd.ArgDataSourceType, pd.Index); } } } #endregion - - UIContextOperation?.Invoke(() => - { - flowEnvironmentEvent.OnProjectLoaded(new ProjectLoadedEventArgs()); - }); - - return; + } #endregion @@ -561,8 +575,27 @@ namespace Serein.NodeFlow.Env #endregion + + + + private async Task TriggerEvent(Action action) + { + if(UIContextOperation is null) + { + action?.Invoke(); + } + else + { + await UIContextOperation.InvokeAsync(() => + { + action?.Invoke(); + }); + } + } } + + } diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index c82cdd6..096ea89 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -51,8 +51,7 @@ namespace Serein.NodeFlow.Env public FlowEnvironment() { ISereinIOC ioc = new SereinIOC(); - ioc.Reset() - .Register(()=> ioc) // 注册IOC + ioc.Register(()=> ioc) // 注册IOC .Register(() => this) .Register() .Register() @@ -205,11 +204,20 @@ namespace Serein.NodeFlow.Env } /// - public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) + public void LoadProject(string filePath) { - if (flowEnvInfo is null) return; + //if (flowEnvInfo is null) return; SetProjectLoadingFlag(false); - currentFlowEnvironment.LoadProject(flowEnvInfo, filePath); + currentFlowEnvironment.LoadProject(filePath); + SetProjectLoadingFlag(true); + } + + /// + public async Task LoadProjetAsync(string filePath) + { + //if (flowEnvInfo is null) return; + SetProjectLoadingFlag(false); + await currentFlowEnvironment.LoadProjetAsync(filePath); SetProjectLoadingFlag(true); } @@ -307,11 +315,7 @@ namespace Serein.NodeFlow.Env /// public void SetUIContextOperation(UIContextOperation uiContextOperation) { - if(uiContextOperation is null) - { - return; - } - IOC.Register(() => uiContextOperation).Build(); + currentFlowEnvironment.SetUIContextOperation(uiContextOperation); } diff --git a/NodeFlow/Env/LocalFlowEnvironment.cs b/NodeFlow/Env/LocalFlowEnvironment.cs index aaa3b2e..c782538 100644 --- a/NodeFlow/Env/LocalFlowEnvironment.cs +++ b/NodeFlow/Env/LocalFlowEnvironment.cs @@ -1,4 +1,6 @@ -using Serein.Library; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Newtonsoft.Json; +using Serein.Library; using Serein.Library.Api; using Serein.Library.FlowNode; using Serein.Library.Utils; @@ -15,6 +17,8 @@ using System.Reactive; using System.Reflection; using System.Security.AccessControl; using System.Text; +using System.Threading.Tasks; +using System.Timers; using static Serein.Library.Api.IFlowEnvironment; namespace Serein.NodeFlow.Env @@ -288,12 +292,75 @@ namespace Serein.NodeFlow.Env /// /// 加载项目文件 /// - /// 环境信息 /// - public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) + public void LoadProject(string filePath) { + string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 + var FlowProjectData = JsonConvert.DeserializeObject(content); + var FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// + + + this.ProjectFileLocation = filePath; - var projectData = flowEnvInfo.Project; + + var projectData = FlowProjectData; + // 加载项目配置文件 + var dllPaths = projectData.Librarys.Select(it => it.FilePath).ToList(); + List methodDetailss = []; + + // 遍历依赖项中的特性注解,生成方法详情 + foreach (var dllPath in dllPaths) + { + string cleanedRelativePath = dllPath.TrimStart('.', '\\'); + var tmpPath = Path.Combine(FileDataPath, cleanedRelativePath); + var dllFilePath = Path.GetFullPath(tmpPath); + LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集 + } + + + + _ = Task.Run(async () => + { + // 加载画布 + try + { + foreach (var canvasInfo in projectData.Canvass) + { + await LoadCanvasAsync(canvasInfo); + } + var nodeInfos = projectData.Nodes.ToList(); + await FlowEdit.LoadNodeInfosAsync(nodeInfos); // 加载节点信息 + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) + { + FlowEdit.SetStartNode(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点 + } + + Event.OnProjectLoaded(new ProjectLoadedEventArgs()); + } + catch (Exception ex) + { + + throw; + } + // + //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 + }); + } + + public async Task LoadProjetAsync(string filePath) + { + string content = await System.IO.File.ReadAllTextAsync(filePath); // 读取整个文件内容 + var FlowProjectData = JsonConvert.DeserializeObject(content); + var FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// + if(FlowProjectData is null) + { + return; + } + + this.ProjectFileLocation = filePath; + var projectData = FlowProjectData; + // 加载项目配置文件 var dllPaths = projectData.Librarys.Select(it => it.FilePath).ToList(); List methodDetailss = []; @@ -309,23 +376,18 @@ namespace Serein.NodeFlow.Env - _ = Task.Run(async () => + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) { - // 加载画布 - foreach (var canvasInfo in projectData.Canvass) - { - LoadCanvas(canvasInfo); - } - await FlowEdit.LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 - - // 加载画布 - foreach (var canvasInfo in projectData.Canvass) - { - FlowEdit.SetStartNode(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点 - } - //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 - }); - + await LoadCanvasAsync(canvasInfo); + } + await FlowEdit.LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) + { + FlowEdit.SetStartNode(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点 + } + Event.OnProjectLoaded(new ProjectLoadedEventArgs()); } /// @@ -532,15 +594,24 @@ namespace Serein.NodeFlow.Env private int _addCanvasCount = 0; - private FlowCanvasDetails LoadCanvas(FlowCanvasDetailsInfo info) + private async Task LoadCanvasAsync(FlowCanvasDetailsInfo info) { var model = new FlowCanvasDetails(this); model.LoadInfo(info); flowModelService.AddCanvasModel(model); - UIContextOperation?.Invoke(() => + + if(UIContextOperation is null) { Event.OnCanvasCreated(new CanvasCreateEventArgs(model)); - }); + } + else + { + await UIContextOperation.InvokeAsync(() => + { + Event.OnCanvasCreated(new CanvasCreateEventArgs(model)); + }); + } + return model; } @@ -585,12 +656,12 @@ namespace Serein.NodeFlow.Env /// public void SetUIContextOperation(UIContextOperation uiContextOperation) { - if (uiContextOperation is not null) + if (uiContextOperation is null) { - this.UIContextOperation = uiContextOperation; - //PersistennceInstance[typeof(UIContextOperation)] = uiContextOperation; // 缓存封装好的UI线程上下文 + return; } - + this.UIContextOperation = uiContextOperation; + IOC.Register(() => uiContextOperation).Build(); } diff --git a/NodeFlow/FlowNodeExtension.cs b/NodeFlow/FlowNodeExtension.cs index f2c12a4..4a26815 100644 --- a/NodeFlow/FlowNodeExtension.cs +++ b/NodeFlow/FlowNodeExtension.cs @@ -5,6 +5,7 @@ using Serein.NodeFlow.Model; using System.Collections.Concurrent; using System.ComponentModel; using System.Reflection; +using System.Text; namespace Serein.NodeFlow { @@ -128,6 +129,32 @@ namespace Serein.NodeFlow } + /// + /// 添加代码 + /// + /// 字符串构建器 + /// 缩进次数(4个空格) + /// 要添加的代码 + /// 字符串构建器本身 + public static StringBuilder AddCode(this StringBuilder sb, + int retractCount = 0, + string code = null) + { + if (!string.IsNullOrWhiteSpace(code)) + { + var retract = new string(' ', retractCount * 4); + sb.AppendLine(retract + code); + } + return sb; + } + + + + + + + + ///// ///// 从节点类型枚举中转为对应的 Model 类型 ///// @@ -165,4 +192,6 @@ namespace Serein.NodeFlow //} } + + } diff --git a/NodeFlow/Model/Node/NodeModelBaseFunc.cs b/NodeFlow/Model/Node/NodeModelBaseFunc.cs index 9a49e37..3cbdb2e 100644 --- a/NodeFlow/Model/Node/NodeModelBaseFunc.cs +++ b/NodeFlow/Model/Node/NodeModelBaseFunc.cs @@ -140,7 +140,7 @@ namespace Serein.NodeFlow.Model } object[] args = await this.GetParametersAsync(context, token); var result = await dd.InvokeAsync(instance, args); - var flowReslt = new FlowResult(this, context, result); + var flowReslt = new FlowResult(this.Guid, context, result); return flowReslt; } diff --git a/NodeFlow/Model/Node/SingleConditionNode.cs b/NodeFlow/Model/Node/SingleConditionNode.cs index ac1b76e..40bbc62 100644 --- a/NodeFlow/Model/Node/SingleConditionNode.cs +++ b/NodeFlow/Model/Node/SingleConditionNode.cs @@ -114,7 +114,7 @@ namespace Serein.NodeFlow.Model { if (token.IsCancellationRequested) { - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } // 接收上一节点参数or自定义参数内容 object? parameter; @@ -128,15 +128,15 @@ namespace Serein.NodeFlow.Model if (hasNode) { context.NextOrientation = ConnectionInvokeType.IsError; - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } if (hasNode) { - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { - result = context.GetFlowData(argSourceNode).Value; // 使用自定义节点的参数 + result = context.GetFlowData(argSourceNode.Guid).Value; // 使用自定义节点的参数 } else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { @@ -148,7 +148,7 @@ namespace Serein.NodeFlow.Model } else { - result = context.TransmissionData(this).Value; // 条件节点透传上一节点的数据 + result = context.TransmissionData(this.Guid).Value; // 条件节点透传上一节点的数据 } parameter = result; // 使用上一节点的参数 @@ -184,7 +184,7 @@ namespace Serein.NodeFlow.Model SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation); //return result; - return new FlowResult(this, context, judgmentResult); + return new FlowResult(this.Guid, context, judgmentResult); } diff --git a/NodeFlow/Model/Node/SingleExpOpNode.cs b/NodeFlow/Model/Node/SingleExpOpNode.cs index 9b07721..96eafce 100644 --- a/NodeFlow/Model/Node/SingleExpOpNode.cs +++ b/NodeFlow/Model/Node/SingleExpOpNode.cs @@ -94,7 +94,7 @@ namespace Serein.NodeFlow.Model public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if(token.IsCancellationRequested) return new FlowResult(this, context); + if(token.IsCancellationRequested) return new FlowResult(this.Guid, context); object? parameter = null;// context.TransmissionData(this); // 表达式节点使用上一节点数据 var pd = MethodDetails.ParameterDetailss[0]; @@ -103,12 +103,12 @@ namespace Serein.NodeFlow.Model if (hasNode) { context.NextOrientation = ConnectionInvokeType.IsError; - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { // 使用自定义节点的参数 - parameter = context.GetFlowData(argSourceNode).Value; + parameter = context.GetFlowData(argSourceNode.Guid).Value; } else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { @@ -122,7 +122,7 @@ namespace Serein.NodeFlow.Model else { // 条件节点透传上一节点的数据 - parameter = context.TransmissionData(this); + parameter = context.TransmissionData(this.Guid); } try @@ -139,13 +139,13 @@ namespace Serein.NodeFlow.Model } context.NextOrientation = ConnectionInvokeType.IsSucceed; - return new FlowResult(this,context, result); + return new FlowResult(this.Guid, context, result); } catch (Exception ex) { context.NextOrientation = ConnectionInvokeType.IsError; context.ExceptionOfRuning = ex; - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } } diff --git a/NodeFlow/Model/Node/SingleFlipflopNode.cs b/NodeFlow/Model/Node/SingleFlipflopNode.cs index c58da1e..1ff3aad 100644 --- a/NodeFlow/Model/Node/SingleFlipflopNode.cs +++ b/NodeFlow/Model/Node/SingleFlipflopNode.cs @@ -64,7 +64,7 @@ namespace Serein.NodeFlow.Model throw new FlipflopException(MethodDetails.MethodName + "触发器超时触发。Guid" + Guid); } object result = dynamicFlipflopContext.Value; - var flowReslt = new FlowResult(this, context, result); + var flowReslt = new FlowResult(this.Guid, context, result); return flowReslt; } diff --git a/NodeFlow/Model/Node/SingleFlowCallNode.cs b/NodeFlow/Model/Node/SingleFlowCallNode.cs index eddc5eb..f50f17c 100644 --- a/NodeFlow/Model/Node/SingleFlowCallNode.cs +++ b/NodeFlow/Model/Node/SingleFlowCallNode.cs @@ -208,6 +208,14 @@ namespace Serein.NodeFlow.Model { TargetNodeGuid = targetNode.Guid; this.targetNode = targetNode; + + if(this.IsShareParam == false) + { + foreach (var item in nodeInfo.ParameterData) + { + + } + } } else { @@ -258,7 +266,7 @@ namespace Serein.NodeFlow.Model // 此处代码与SereinFlow.Library.FlowNode.ParameterDetails // ToMethodArgData()方法中判断流程接口节点分支逻辑耦合 // 不要轻易修改 - context.AddOrUpdate(targetNode, flowData); + context.AddOrUpdate(targetNode.Guid, flowData); foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) { if (this.SuccessorNodes[ctType] == null) continue; @@ -267,7 +275,7 @@ namespace Serein.NodeFlow.Model if (node.DebugSetting.IsEnable) { - context.SetPreviousNode(node, this); + context.SetPreviousNode(node.Guid, this.Guid); } } } diff --git a/NodeFlow/Model/Node/SingleGlobalDataNode.cs b/NodeFlow/Model/Node/SingleGlobalDataNode.cs index 27f1064..f8912b5 100644 --- a/NodeFlow/Model/Node/SingleGlobalDataNode.cs +++ b/NodeFlow/Model/Node/SingleGlobalDataNode.cs @@ -110,18 +110,18 @@ namespace Serein.NodeFlow.Model /// public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if (token.IsCancellationRequested) return new FlowResult(this, context); + if (token.IsCancellationRequested) return new FlowResult(this.Guid, context); if (string.IsNullOrEmpty(KeyName)) { context.NextOrientation = ConnectionInvokeType.IsError; SereinEnv.WriteLine(InfoType.ERROR, $"全局数据的KeyName不能为空[{this.Guid}]"); - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } if (DataNode is null) { context.NextOrientation = ConnectionInvokeType.IsError; SereinEnv.WriteLine(InfoType.ERROR, $"全局数据节点没有设置数据来源[{this.Guid}]"); - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } try @@ -135,7 +135,7 @@ namespace Serein.NodeFlow.Model { context.NextOrientation = ConnectionInvokeType.IsError; context.ExceptionOfRuning = ex; - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } } diff --git a/NodeFlow/Model/Node/SingleScriptNode.cs b/NodeFlow/Model/Node/SingleScriptNode.cs index 0956ace..1ce124c 100644 --- a/NodeFlow/Model/Node/SingleScriptNode.cs +++ b/NodeFlow/Model/Node/SingleScriptNode.cs @@ -136,6 +136,8 @@ namespace Serein.NodeFlow.Model this.MethodDetails.ParameterDetailss[i].Name = nodeInfo.ParameterData[i].ArgName; } + ReloadScript();// 加载时重新解析 + IsScriptChanged = false; // 重置脚本改变标志 } @@ -165,6 +167,8 @@ namespace Serein.NodeFlow.Model var p = new SereinScriptParser(sb.ToString()); //var p = new SereinScriptParser(Script); mainNode = p.Parse(); // 开始解析 + + } catch (Exception ex) { @@ -192,9 +196,9 @@ namespace Serein.NodeFlow.Model /// public async Task ExecutingAsync(NodeModelBase flowCallNode, IDynamicContext context, CancellationToken token) { - if (token.IsCancellationRequested) return new FlowResult(this, context); + if (token.IsCancellationRequested) return new FlowResult(this.Guid, context); var @params = await flowCallNode.GetParametersAsync(context, token); - if (token.IsCancellationRequested) return new FlowResult(this, context); + if (token.IsCancellationRequested) return new FlowResult(this.Guid, context); //context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改 @@ -204,7 +208,8 @@ namespace Serein.NodeFlow.Model if (IsScriptChanged) { ReloadScript();// 每次都重新解析 - IsScriptChanged = false; + IsScriptChanged = false; + context.Env.WriteLine(InfoType.INFO, $"[{Guid}]脚本解析完成"); } } } @@ -233,7 +238,7 @@ namespace Serein.NodeFlow.Model var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行 envEvent.FlowRunComplete -= onFlowStop; - return new FlowResult(this, context, result); + return new FlowResult(this.Guid, context, result); } #region 挂载的方法 diff --git a/NodeFlow/Model/Node/SingleUINode.cs b/NodeFlow/Model/Node/SingleUINode.cs index ceac5c7..8872d7b 100644 --- a/NodeFlow/Model/Node/SingleUINode.cs +++ b/NodeFlow/Model/Node/SingleUINode.cs @@ -17,7 +17,7 @@ namespace Serein.NodeFlow.Model public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) { - if (token.IsCancellationRequested) return new FlowResult(this,context); + if (token.IsCancellationRequested) return new FlowResult(this.Guid, context); if(Adapter is null) { @@ -34,13 +34,13 @@ namespace Serein.NodeFlow.Model } else { - var p = context.GetPreviousNode(this); + var p = context.GetPreviousNode(this.Guid); var data = context.GetFlowData(p).Value; var iflowContorl = Adapter.GetFlowControl(); iflowContorl.OnExecuting(data); } - return new FlowResult(this, context); + return new FlowResult(this.Guid, context); } } } diff --git a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs index f8f194a..434bf0e 100644 --- a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs +++ b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs @@ -89,7 +89,7 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; if (!flowModelService.TryGetCanvasModel(CanvasGuid, out FlowCanvas) // 不存在画布 @@ -132,12 +132,12 @@ namespace Serein.NodeFlow.Model.Operation // flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除 //} - var state = (JunctionOfConnectionType, ChangeType) switch + var state = (JunctionOfConnectionType, ChangeType) switch { - (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Create) => CreateInvokeConnection(), // 创建节点之间的调用关系 - (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => RemoveInvokeConnection(), // 移除节点之间的调用关系 - (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Create) => CreateArgConnection(), // 创建节点之间的参数传递关系 - (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => RemoveArgConnection(), // 移除节点之间的参数传递关系 + (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Create) => await CreateInvokeConnection(), // 创建节点之间的调用关系 + (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => await RemoveInvokeConnection(), // 移除节点之间的调用关系 + (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Create) => await CreateArgConnection(), // 创建节点之间的参数传递关系 + (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => await RemoveArgConnection(), // 移除节点之间的参数传递关系 _ => false }; return state; @@ -151,7 +151,7 @@ namespace Serein.NodeFlow.Model.Operation /// /// 创建方法调用关系 /// - private bool CreateInvokeConnection() + private async Task CreateInvokeConnection() { IFlowNode fromNode = FromNode ; IFlowNode toNode = ToNode; @@ -224,15 +224,20 @@ namespace Serein.NodeFlow.Model.Operation } fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点新类别的子分支 toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点新类别的父分支 - flowEnvironmentEvent.OnNodeConnectChanged( - new NodeConnectChangeEventArgs( - FlowCanvas.Guid, - fromNode.Guid, // 从哪个节点开始 - toNode.Guid, // 连接到那个节点 - JunctionOfConnectionType.Invoke, - invokeType, // 连接线的样式类型 - NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 - )); + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged( + new NodeConnectChangeEventArgs( + FlowCanvas.Guid, + fromNode.Guid, // 从哪个节点开始 + toNode.Guid, // 连接到那个节点 + JunctionOfConnectionType.Invoke, + invokeType, // 连接线的样式类型 + NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 + )); + }); + // Invoke // GetResult return true; @@ -248,19 +253,22 @@ namespace Serein.NodeFlow.Model.Operation /// /// 移除方法调用关系 /// - private bool RemoveInvokeConnection() + private async Task RemoveInvokeConnection() { FromNode.SuccessorNodes[ConnectionInvokeType].Remove(ToNode); ToNode.PreviousNodes[ConnectionInvokeType].Remove(FromNode); - flowEnvironmentEvent.OnNodeConnectChanged( - new NodeConnectChangeEventArgs( - FlowCanvas.Guid, - FromNode.Guid, - ToNode.Guid, - JunctionOfConnectionType.Invoke, - ConnectionInvokeType, - NodeConnectChangeEventArgs.ConnectChangeType.Remove)); + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged( + new NodeConnectChangeEventArgs( + FlowCanvas.Guid, + FromNode.Guid, + ToNode.Guid, + JunctionOfConnectionType.Invoke, + ConnectionInvokeType, + NodeConnectChangeEventArgs.ConnectChangeType.Remove)); + }); /* if (string.IsNullOrEmpty(ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid)) @@ -289,7 +297,7 @@ namespace Serein.NodeFlow.Model.Operation /// 创建参数连接关系 /// /// - private bool CreateArgConnection() + private async Task CreateArgConnection() { IFlowNode fromNodeControl = ToNode; IFlowNode toNodeControl = ToNode; @@ -304,43 +312,27 @@ namespace Serein.NodeFlow.Model.Operation if (FromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == ConnectionArgSourceType) { - SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系,此次操作将不会执行" + + SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系" + $"起始节点:{FromNode.Guid}" + $"目标节点:{ToNode.Guid}" + $"参数索引:{ArgIndex}" + $"参数类型:{ConnectionArgSourceType}"); - /*flowEnvironmentEvent.OnNodeConnectChanged( - new NodeConnectChangeEventArgs( - FlowCanvas.Guid, - FromNode.Guid, // 从哪个节点开始 - ToNode.Guid, // 连接到那个节点 - ArgIndex, // 连接线的样式类型 - JunctionOfConnectionType.Arg, - ConnectionArgSourceType, - NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 - )); // 通知UI */ - return true; - } - - if (!string.IsNullOrEmpty(toNodeArgSourceGuid)) // 更改关系获取 - { - ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null; - ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 - flowEnvironmentEvent.OnNodeConnectChanged( - new NodeConnectChangeEventArgs( - FlowCanvas.Guid, - FromNode.Guid, - ToNode.Guid, - ArgIndex, - JunctionOfConnectionType.Arg, - ConnectionArgSourceType.GetPreviousNodeData, - NodeConnectChangeEventArgs.ConnectChangeType.Remove)); - } - - ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = FromNode.Guid; // 设置 - ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType; - - flowEnvironmentEvent.OnNodeConnectChanged( + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged( + new NodeConnectChangeEventArgs( + FlowCanvas.Guid, + FromNode.Guid, // 从哪个节点开始 + ToNode.Guid, // 连接到那个节点 + ArgIndex, // 连接线的样式类型 + JunctionOfConnectionType.Arg, + ConnectionArgSourceType, + NodeConnectChangeEventArgs.ConnectChangeType.Remove // 是创建连接还是删除连接 + )); // 通知UI + }); + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged( new NodeConnectChangeEventArgs( FlowCanvas.Guid, FromNode.Guid, // 从哪个节点开始 @@ -350,6 +342,47 @@ namespace Serein.NodeFlow.Model.Operation ConnectionArgSourceType, NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 )); // 通知UI + }); + + return true; + } + + if (!string.IsNullOrEmpty(toNodeArgSourceGuid)) // 更改关系获取 + { + ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null; + ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged( + new NodeConnectChangeEventArgs( + FlowCanvas.Guid, + FromNode.Guid, + ToNode.Guid, + ArgIndex, + JunctionOfConnectionType.Arg, + ConnectionArgSourceType.GetPreviousNodeData, + NodeConnectChangeEventArgs.ConnectChangeType.Remove)); + }); + } + + ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = FromNode.Guid; // 设置 + ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType; + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged( + new NodeConnectChangeEventArgs( + FlowCanvas.Guid, + FromNode.Guid, // 从哪个节点开始 + ToNode.Guid, // 连接到那个节点 + ArgIndex, // 连接线的样式类型 + JunctionOfConnectionType.Arg, + ConnectionArgSourceType, + NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 + )); // 通知UI + }); + return true; } @@ -360,14 +393,15 @@ namespace Serein.NodeFlow.Model.Operation /// /// /// - private bool RemoveArgConnection() + private async Task RemoveArgConnection() { ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null; ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 - if (OperatingSystem.IsWindows()) + + await TriggerEvent(() => { - flowEnvironmentEvent.OnNodeConnectChanged( + flowEnvironmentEvent.OnNodeConnectChanged( new NodeConnectChangeEventArgs( FlowCanvas.Guid, FromNode.Guid, @@ -376,7 +410,8 @@ namespace Serein.NodeFlow.Model.Operation JunctionOfConnectionType.Arg, ConnectionArgSourceType.GetPreviousNodeData, NodeConnectChangeEventArgs.ConnectChangeType.Remove)); - } + }); + return true; } diff --git a/NodeFlow/Model/Operation/ChangeParameterOperation.cs b/NodeFlow/Model/Operation/ChangeParameterOperation.cs index 6269fc1..5cd2d93 100644 --- a/NodeFlow/Model/Operation/ChangeParameterOperation.cs +++ b/NodeFlow/Model/Operation/ChangeParameterOperation.cs @@ -52,30 +52,31 @@ namespace Serein.NodeFlow.Model.Operation } - public override bool Execute() + public override Task ExecuteAsync() { - if (!ValidationParameter()) return false; + + if (!ValidationParameter()) return Task.FromResult(false); if (IsAdd) { if (nodeModel.MethodDetails.AddParamsArg(ParamIndex)) { - return true; + return Task.FromResult(true); } else { - return false; + return Task.FromResult(false); } } else { if (nodeModel.MethodDetails.RemoveParamsArg(ParamIndex)) { - return true; + return Task.FromResult(true); } else { - return true; + return Task.FromResult(true); } } } diff --git a/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs b/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs index 828d5b8..9dc6b4e 100644 --- a/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs +++ b/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs @@ -1,8 +1,11 @@ -using Serein.Library.Api; +using Microsoft.VisualBasic.FileIO; +using Serein.Library.Api; +using Serein.Library.Utils; using Serein.NodeFlow.Services; using System; using System.Collections.Generic; using System.Linq; +using System.Net.Mime; using System.Text; using System.Threading.Tasks; @@ -72,12 +75,16 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; ContainerNode.PlaceNode(Node); - flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, ContainerNodeGuid)); // 通知UI更改节点放置位置 + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, ContainerNodeGuid)); // 通知UI更改节点放置位置 + }); return true; } diff --git a/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs b/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs index 650dd91..0d885ff 100644 --- a/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs +++ b/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs @@ -61,12 +61,15 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; ContainerNode.TakeOutNode(Node); - flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, NodeGuid)); // 重新放置在画布上 + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, NodeGuid)); // 重新放置在画布上 + }); return true; } diff --git a/NodeFlow/Model/Operation/CreateCanvasOperation.cs b/NodeFlow/Model/Operation/CreateCanvasOperation.cs index 747f6b1..595946f 100644 --- a/NodeFlow/Model/Operation/CreateCanvasOperation.cs +++ b/NodeFlow/Model/Operation/CreateCanvasOperation.cs @@ -30,7 +30,7 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if(!ValidationParameter()) return false; @@ -38,7 +38,11 @@ namespace Serein.NodeFlow.Model.Operation cavasnModel.LoadInfo(CanvasInfo); flowModelService.AddCanvasModel(cavasnModel); this.flowCanvasDetails = cavasnModel; ; - flowEnvironmentEvent.OnCanvasCreated(new CanvasCreateEventArgs(cavasnModel)); + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnCanvasCreated(new CanvasCreateEventArgs(cavasnModel)); + }); return true; } diff --git a/NodeFlow/Model/Operation/CreateNodeOperation.cs b/NodeFlow/Model/Operation/CreateNodeOperation.cs index e688504..bf61998 100644 --- a/NodeFlow/Model/Operation/CreateNodeOperation.cs +++ b/NodeFlow/Model/Operation/CreateNodeOperation.cs @@ -66,7 +66,7 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; // 执行时验证 @@ -101,7 +101,11 @@ namespace Serein.NodeFlow.Model.Operation flowModelService.AddNodeModel(nodeModel); this.flowNode = nodeModel; - flowEnvironmentEvent.OnNodeCreated(new NodeCreateEventArgs(flowCanvasDetails.Guid, nodeModel, Position)); + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeCreated(new NodeCreateEventArgs(flowCanvasDetails.Guid, nodeModel, Position)); + }); return true; } diff --git a/NodeFlow/Model/Operation/OperationBase.cs b/NodeFlow/Model/Operation/OperationBase.cs index a74d824..1e4c501 100644 --- a/NodeFlow/Model/Operation/OperationBase.cs +++ b/NodeFlow/Model/Operation/OperationBase.cs @@ -1,5 +1,6 @@ using Serein.Library; using Serein.Library.Api; +using Serein.Library.Utils; using Serein.NodeFlow.Services; using Serein.NodeFlow.Tool; using System; @@ -25,7 +26,7 @@ namespace Serein.NodeFlow.Model.Operation /// /// 执行操作 /// - bool Execute(); + Task ExecuteAsync(); /// /// 撤销操作 /// @@ -46,6 +47,12 @@ namespace Serein.NodeFlow.Model.Operation [AutoInjection] protected FlowModelService flowModelService; + /// + /// 节点管理服务 + /// + [AutoInjection] + protected UIContextOperation uiContextOperation; + /// /// 流程依赖服务 /// @@ -75,7 +82,7 @@ namespace Serein.NodeFlow.Model.Operation /// /// 执行 /// - public abstract bool Execute(); + public abstract Task ExecuteAsync(); /// /// 撤销 @@ -96,6 +103,23 @@ namespace Serein.NodeFlow.Model.Operation public abstract void ToInfo(); + protected async Task TriggerEvent(Action action) + { + /* if (OperatingSystem.IsWindows()) + { + }*/ + if (uiContextOperation is null) + { + action?.Invoke(); + } + else + { + await uiContextOperation.InvokeAsync(() => + { + action?.Invoke(); + }); + } + } } diff --git a/NodeFlow/Model/Operation/RemoveCanvasOperation.cs b/NodeFlow/Model/Operation/RemoveCanvasOperation.cs index fc25d4a..0d5464b 100644 --- a/NodeFlow/Model/Operation/RemoveCanvasOperation.cs +++ b/NodeFlow/Model/Operation/RemoveCanvasOperation.cs @@ -31,7 +31,7 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; @@ -46,7 +46,11 @@ namespace Serein.NodeFlow.Model.Operation flowModelService.RemoveCanvasModel(flowCanvasDetails); flowCanvasDetailsInfo = flowCanvasDetails.ToInfo(); - flowEnvironmentEvent.OnCanvasRemoved(new CanvasRemoveEventArgs(flowCanvasDetails.Guid)); + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnCanvasRemoved(new CanvasRemoveEventArgs(flowCanvasDetails.Guid)); + }); return true; } diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operation/RemoveNodeOperation.cs index d99d014..2a363b9 100644 --- a/NodeFlow/Model/Operation/RemoveNodeOperation.cs +++ b/NodeFlow/Model/Operation/RemoveNodeOperation.cs @@ -50,7 +50,7 @@ namespace Serein.NodeFlow.Model.Operation return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; @@ -74,8 +74,10 @@ namespace Serein.NodeFlow.Model.Operation connectionType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - flowEnvironmentEvent.OnNodeConnectChanged(e); - + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged(e); + }); } } @@ -102,7 +104,10 @@ namespace Serein.NodeFlow.Model.Operation connectionType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - flowEnvironmentEvent.OnNodeConnectChanged(e); + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged(e); + }); } } } @@ -136,7 +141,10 @@ namespace Serein.NodeFlow.Model.Operation connectionType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线 EventArgs.Add(e); // 缓存事件参数 - flowEnvironmentEvent.OnNodeConnectChanged(e); + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeConnectChanged(e); + }); } } } @@ -153,12 +161,16 @@ namespace Serein.NodeFlow.Model.Operation { // 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上 // 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行 - flowEnvironment.UIContextOperation?.Invoke(() => + await TriggerEvent(() => { flowCanvasDetails?.Nodes.Remove(flowNode); }); } - flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid)); + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid)); + }); return true; } diff --git a/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs b/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs index 8b0e4ce..a8a6772 100644 --- a/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs +++ b/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs @@ -46,9 +46,9 @@ namespace Serein.NodeFlow.Model.Operation /// /// 成为首项 /// - public override bool Execute() + public override Task ExecuteAsync() { - if(!ValidationParameter()) return false; + if(!ValidationParameter()) return Task.FromResult(false); if (FromNode.SuccessorNodes.TryGetValue(ConnectionType, out var nodes)) { @@ -60,7 +60,7 @@ namespace Serein.NodeFlow.Model.Operation nodes.Insert(0, ToNode); } } - return true; + return Task.FromResult(true); } /// diff --git a/NodeFlow/Model/Operation/SetStartNodeOperation.cs b/NodeFlow/Model/Operation/SetStartNodeOperation.cs index 184c1e1..5dfb7a7 100644 --- a/NodeFlow/Model/Operation/SetStartNodeOperation.cs +++ b/NodeFlow/Model/Operation/SetStartNodeOperation.cs @@ -34,7 +34,7 @@ namespace Serein.NodeFlow.Model.Operation } return true; } - public override bool Execute() + public override async Task ExecuteAsync() { if (!ValidationParameter()) return false; @@ -45,7 +45,11 @@ namespace Serein.NodeFlow.Model.Operation } CanvasModel.StartNode = NewStartNodeModel; - flowEnvironmentEvent.OnStartNodeChanged(new StartNodeChangeEventArgs(CanvasModel.Guid, OldStartNodeModel?.Guid, NewStartNodeModel.Guid)); + + await TriggerEvent(() => + { + flowEnvironmentEvent.OnStartNodeChanged(new StartNodeChangeEventArgs(CanvasModel.Guid, OldStartNodeModel?.Guid, NewStartNodeModel.Guid)); + }); return true; } diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 60d8a0f..d2ae59b 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -68,6 +68,10 @@ + + + + diff --git a/NodeFlow/Services/FlowModelService.cs b/NodeFlow/Services/FlowModelService.cs index d40f7d1..3cf039b 100644 --- a/NodeFlow/Services/FlowModelService.cs +++ b/NodeFlow/Services/FlowModelService.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Xml.Linq; namespace Serein.NodeFlow.Services { @@ -113,5 +114,53 @@ namespace Serein.NodeFlow.Services return flowCanvasDetails.Nodes.Count > 0; } + + public void ToCsharpCoreFile() + { + // TODO: 实现将流程模型转换为C# Core文件的逻辑 + // 遍历每个画布 + int canvas_index = 0; + + HashSet assemblyFlowClasss = new HashSet(); // 用于创建依赖注入项 + StringBuilder stringBuilder = new StringBuilder(); + + foreach (var canvas in FlowCanvass.Values) + { + int flowTemplateId = canvas_index++; + string flowTemplateClassName = $"FlowTemplate{flowTemplateId}"; + + HashSet flowClasss = new HashSet(); + + // 收集程序集信息 + foreach (var node in canvas.Nodes) + { + var instanceType = node.MethodDetails.ActingInstanceType; + if(instanceType is not null) + { + flowClasss.Add(instanceType); + assemblyFlowClasss.Add(instanceType); + } + } + + // 生成方法信息 + foreach (var node in canvas.Nodes) + { + var instanceType = node.MethodDetails.ActingInstanceType; + var returnType = node.MethodDetails.ReturnType; + var methodName = node.MethodDetails.MethodAnotherName; + + + + var instanceTypeFullName = instanceType.FullName; + var returnTypeFullName = returnType == typeof(void) ? "void" : returnType.FullName; + + string methodContext = $"private {returnTypeFullName} NodeMethod_{methodName}({nameof(IDynamicContext)} context)"; + SereinEnv.WriteLine(InfoType.INFO, methodContext); + } + } + } + + + } } diff --git a/NodeFlow/Services/FlowOperationService.cs b/NodeFlow/Services/FlowOperationService.cs index b5fe287..57865f7 100644 --- a/NodeFlow/Services/FlowOperationService.cs +++ b/NodeFlow/Services/FlowOperationService.cs @@ -50,12 +50,12 @@ namespace Serein.NodeFlow.Services /// /// 重做 /// - public void Redo() + public async Task Redo() { if (redoStack.Count > 0) { var command = redoStack.Pop(); - var state = command.Execute(); + var state = await command.ExecuteAsync(); if (state) { undoStack.Push(command); // 将重做的命令推入撤销栈 @@ -64,10 +64,10 @@ namespace Serein.NodeFlow.Services } - internal void Execute(IOperation operation) + internal async Task Execute(IOperation operation) { sereinIOC.InjectDependenciesProperty(operation); // 注入所需要的依赖 - var state = operation.Execute(); + var state = await operation.ExecuteAsync(); if (state) { // 执行后,推入撤销栈,并清空重做栈 diff --git a/NodeFlow/Services/FlowWorkManagement.cs b/NodeFlow/Services/FlowWorkManagement.cs index 7f47232..e664b44 100644 --- a/NodeFlow/Services/FlowWorkManagement.cs +++ b/NodeFlow/Services/FlowWorkManagement.cs @@ -273,14 +273,11 @@ namespace Serein.NodeFlow.Services var pool = WorkOptions.FlowContextPool; var context = pool.Allocate(); var token = WorkOptions.CancellationTokenSource.Token; - await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行 + var result = await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行 context.Reset(); pool.Free(context); } - - - /// /// 尝试添加全局触发器 /// @@ -295,6 +292,7 @@ namespace Serein.NodeFlow.Services } } + /// /// 尝试移除全局触发器 /// @@ -344,7 +342,7 @@ namespace Serein.NodeFlow.Services { var context = pool.Allocate(); // 启动全局触发器时新建上下文 var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task - context.AddOrUpdate(singleFlipFlopNode, newFlowData); + context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData); if (context.NextOrientation == ConnectionInvokeType.None) { continue; @@ -388,7 +386,7 @@ namespace Serein.NodeFlow.Services { continue; } - context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); // 设置调用关系 + context.SetPreviousNode(nextNodes[i].Guid, singleFlipFlopNode.Guid); // 设置调用关系 if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前检查终端 { @@ -407,7 +405,7 @@ namespace Serein.NodeFlow.Services continue; } - context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); + context.SetPreviousNode(nextNodes[i].Guid, singleFlipFlopNode.Guid); if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 { await nextNodes[i].DebugSetting.GetInterruptTask.Invoke(); diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs index f044daa..30ce730 100644 --- a/Workbench/App.xaml.cs +++ b/Workbench/App.xaml.cs @@ -9,9 +9,11 @@ using Serein.Workbench.Services; using Serein.Workbench.ViewModels; using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using System.Windows; using System.Windows.Input; using System.Windows.Threading; +using static System.Net.Mime.MediaTypeNames; namespace Serein.Workbench { @@ -20,7 +22,7 @@ namespace Serein.Workbench /// /// Interaction logic for App.xaml /// - public partial class App : Application + public partial class App : System.Windows.Application { private static IServiceProvider? ServiceProvider; @@ -28,12 +30,12 @@ namespace Serein.Workbench /// UI线程 /// public static UIContextOperation UIContextOperation => App.GetService() ?? throw new NullReferenceException(); - + public static T GetService() where T : class { return ServiceProvider?.GetService() ?? throw new NullReferenceException(); } - + public App() { var collection = new ServiceCollection(); diff --git a/Workbench/Serein.WorkBench.csproj b/Workbench/Serein.WorkBench.csproj index cdb1c5c..88ec9f3 100644 --- a/Workbench/Serein.WorkBench.csproj +++ b/Workbench/Serein.WorkBench.csproj @@ -75,7 +75,7 @@ - + diff --git a/Workbench/ServiceCollectionExtensions.cs b/Workbench/ServiceCollectionExtensions.cs index 013ac8a..a27b616 100644 --- a/Workbench/ServiceCollectionExtensions.cs +++ b/Workbench/ServiceCollectionExtensions.cs @@ -63,10 +63,12 @@ namespace Serein.Workbench getSyncContext = () => uiContext; } }); - UIContextOperation? uIContextOperation = null; - uIContextOperation = new UIContextOperation(getSyncContext); // 封装一个调用UI线程的工具类 - var flowEnvironment = new FlowEnvironment(); + UIContextOperation? uIContextOperation = new (getSyncContext); // 封装一个调用UI线程的工具类 + IFlowEnvironment flowEnvironment = new FlowEnvironment(); flowEnvironment.SetUIContextOperation(uIContextOperation); + + + collection.AddSingleton(uIContextOperation); // 注册UI线程操作上下文 collection.AddSingleton(flowEnvironment); // 注册运行环境 collection.AddSingleton(flowEnvironment.Event); // 注册运行环境事件 diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs index baf4544..0c3ceb6 100644 --- a/Workbench/Services/FlowEEForwardingService.cs +++ b/Workbench/Services/FlowEEForwardingService.cs @@ -14,6 +14,7 @@ using Serein.Library.Utils; using Serein.Workbench.Avalonia.Api; using Serein.Workbench.Api; using System.Diagnostics; +using static System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel; namespace Serein.Workbench.Services { @@ -184,10 +185,11 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value) { - uiContextOperation.Invoke(() => + + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => { EnvOutput?.Invoke(type, value); - }); + })); } /// @@ -197,7 +199,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnProjectSaving(ProjectSavingEventArgs eventArgs) { - ProjectSaving?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + ProjectSaving?.Invoke(eventArgs); + })); } /// @@ -206,7 +211,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnProjectLoaded(ProjectLoadedEventArgs eventArgs) { - ProjectLoaded?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + ProjectLoaded?.Invoke(eventArgs); + })); } /// @@ -216,8 +224,11 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnFlowRunCompleteEvent(FlowEventArgs eventArgs) { - SereinEnv.WriteLine(InfoType.INFO, "-------运行完成---------\r\n"); - FlowRunComplete?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + SereinEnv.WriteLine(InfoType.INFO, "-------运行完成---------\r\n"); + FlowRunComplete?.Invoke(eventArgs); + })); } /// @@ -225,7 +236,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_DllLoadEvent(LoadDllEventArgs eventArgs) { - DllLoad?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + DllLoad?.Invoke(eventArgs); + })); } /// @@ -234,11 +248,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs) { - uiContextOperation.Invoke(() => + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => { - Debug.WriteLine(DateTime.Now, $"Node Connect Changed"); NodeConnectChanged?.Invoke(eventArgs); - }); + })); } @@ -249,10 +262,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironmentEvent_OnCanvasCreate(CanvasCreateEventArgs eventArgs) { - uiContextOperation?.Invoke(() => + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => { CanvasCreated?.Invoke(eventArgs); - }); + })); } /// @@ -262,7 +275,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironmentEvent_OnCanvasRemove(CanvasRemoveEventArgs eventArgs) { - CanvasRemoved?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + CanvasRemoved?.Invoke(eventArgs); + })); } @@ -272,7 +288,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_NodeRemoveEvent(NodeRemoveEventArgs eventArgs) { - NodeRemoved?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + NodeRemoved?.Invoke(eventArgs); + })); } /// @@ -282,11 +301,11 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs) { - uiContextOperation.Invoke(() => + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => { Debug.WriteLine(DateTime.Now, $"Create Node {eventArgs.NodeModel.Guid}"); NodeCreated?.Invoke(eventArgs); - }); + })); } /// @@ -296,7 +315,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnNodePlaceEvent(NodePlaceEventArgs eventArgs) { - NodePlace?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + NodePlace?.Invoke(eventArgs); + })); } /// @@ -305,19 +327,23 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnNodeTakeOutEvent(NodeTakeOutEventArgs eventArgs) { - NodeTakeOut?.Invoke(eventArgs); - + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + NodeTakeOut?.Invoke(eventArgs); + })); } /// /// 设置了流程起始控件 /// - /// - /// + /// + private void FlowEnvironment_StartNodeChangeEvent(StartNodeChangeEventArgs eventArgs) { - - StartNodeChanged?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + StartNodeChanged?.Invoke(eventArgs); + })); } /// @@ -326,7 +352,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnMonitorObjectChangeEvent(MonitorObjectEventArgs eventArgs) { - MonitorObjectChanged?.Invoke(eventArgs); + System.Windows.Application.Current.Dispatcher.Invoke((Action)(() => + { + MonitorObjectChanged?.Invoke(eventArgs); + })); } /// diff --git a/Workbench/Services/FlowProjectService.cs b/Workbench/Services/FlowProjectService.cs index bee1c80..7af1b17 100644 --- a/Workbench/Services/FlowProjectService.cs +++ b/Workbench/Services/FlowProjectService.cs @@ -15,8 +15,6 @@ namespace Serein.Workbench.Services { private readonly IFlowEnvironment flowEnvironment; - public SereinProjectData? FlowProjectData { get; set; } - public string FileDataPath { get; set; } public FlowProjectService(IFlowEnvironment flowEnvironment) { @@ -32,15 +30,13 @@ namespace Serein.Workbench.Services { if (File.Exists(filePath)) { - string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 - this.FlowProjectData = JsonConvert.DeserializeObject(content); - this.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// + /* var dir = Path.GetDirectoryName(filePath); var flowEnvInfo = new FlowEnvInfo { Project = FlowProjectData, - }; - flowEnvironment.LoadProject(flowEnvInfo, FileDataPath); + };*/ + flowEnvironment.LoadProject(filePath); } }