diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 12d8fa4..7577c5f 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -135,6 +135,18 @@ namespace Serein.Library this.NodeModel = nodeModel; } + public ParameterDetails(ParameterData pdInfo, int argIndex) + { + this.Index = argIndex; + this.DataType = typeof(object); + this.ArgDataSourceNodeGuid = pdInfo.SourceNodeGuid; + this.ArgDataSourceType = EnumHelper.ConvertEnum(pdInfo.SourceType); + this.DataValue = pdInfo.Value; + this.InputType = ParameterValueInputType.Input; + this.IsExplicitData = pdInfo.State; + this.Name = pdInfo.ArgName; + } + /// /// 通过参数信息加载实体,用于加载项目文件、远程连接的场景 /// @@ -223,7 +235,7 @@ namespace Serein.Library } #endregion #region “枚举-类型”转换器 - if (ExplicitType.IsEnum && DataType != ExplicitType) + if (ExplicitType is not null && ExplicitType.IsEnum && DataType != ExplicitType) { var resultEnum = Enum.Parse(ExplicitType, DataValue); // 获取绑定的类型 diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 260eb95..0a59a2d 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -878,61 +878,6 @@ namespace Serein.NodeFlow.Env return false; } - /// - /// 从节点信息创建节点,并返回状态指示是否创建成功 - /// - /// - /// - private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo) - { - if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) - { - return false; - } - - #region 获取方法描述 - MethodDetails? methodDetails; - if (controlType.IsBaseNode()) - { - // 加载基础节点 - methodDetails = new MethodDetails(); - } - else - { - if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false; - // 加载方法节点 - FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 - } - #endregion - - var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 - if (nodeModel is null) - { - nodeInfo.Guid = string.Empty; - return false; - } - if (FlowCanvass.TryGetValue(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(() => - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 - return true; - } - /// /// 从节点信息集合批量加载节点控件 @@ -1127,13 +1072,6 @@ namespace Serein.NodeFlow.Env // 通知UI更改 UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position))); - - // 因为需要UI先布置了元素,才能通知UI变更特效 - // 如果不存在流程起始控件,默认设置为流程起始控件 - if (canvasModel.StartNode is null) - { - SetStartNode(canvasModel, nodeModel); - } var nodeInfo = nodeModel.ToInfo(); return Task.FromResult(nodeInfo); } @@ -1764,9 +1702,88 @@ namespace Serein.NodeFlow.Env - }*/ + }*/ #endregion + + /// + /// 从节点信息创建节点,并返回状态指示是否创建成功 + /// + /// + /// + private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo) + { + if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) + { + return false; + } + + #region 获取方法描述 + MethodDetails? methodDetails; + if (controlType == NodeControlType.FlowCall) + { + if (string.IsNullOrEmpty(nodeInfo.MethodName)) + { + methodDetails = new MethodDetails(); + methodDetails.ParamsArgIndex = 0; + methodDetails.ParameterDetailss = new ParameterDetails[nodeInfo.ParameterData.Length]; + for (int i = 0; i < methodDetails.ParameterDetailss.Length; i++) + { + var pdInfo = nodeInfo.ParameterData[i]; + var t = new ParameterDetailsInfo(); + var pd = new ParameterDetails(pdInfo, i); + methodDetails.ParameterDetailss[i] = pd; + } + } + else + { + // 目标节点可能是方法节点 + FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 + } + } + else if (controlType.IsBaseNode()) + { + // 加载基础节点 + methodDetails = new MethodDetails(); + + } + else + { + if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false; + // 加载方法节点 + FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息 + } + #endregion + + var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 + if (nodeModel is null) + { + nodeInfo.Guid = string.Empty; + return false; + } + if (FlowCanvass.TryGetValue(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(() => + OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 + return true; + } + + /// /// 移除连接关系 /// diff --git a/NodeFlow/FlowNodeExtension.cs b/NodeFlow/FlowNodeExtension.cs index 538036b..8056b78 100644 --- a/NodeFlow/FlowNodeExtension.cs +++ b/NodeFlow/FlowNodeExtension.cs @@ -20,10 +20,7 @@ namespace Serein.NodeFlow /// public static bool IsBaseNode(this NodeControlType nodeControlType) { - if(nodeControlType == NodeControlType.FlowCall) - { - return false; - } + var nodeDesc = EnumHelper.GetAttribute(nodeControlType); if("base".Equals(nodeDesc?.Description, StringComparison.OrdinalIgnoreCase)) { diff --git a/NodeFlow/Model/SingleFlowCallNode.cs b/NodeFlow/Model/SingleFlowCallNode.cs index c3351de..0302817 100644 --- a/NodeFlow/Model/SingleFlowCallNode.cs +++ b/NodeFlow/Model/SingleFlowCallNode.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json.Linq; using Serein.Library; using Serein.Library.Api; +using Serein.Script; using System; using System.Collections.Generic; using System.Dynamic; @@ -88,10 +89,6 @@ namespace Serein.NodeFlow.Model // 取消设置接口节点 targetNode.PropertyChanged -= TargetNode_PropertyChanged; this.MethodDetails = new MethodDetails(); - /*foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) - { - this.SuccessorNodes[ctType] = new List(); - }*/ } else { @@ -99,20 +96,21 @@ namespace Serein.NodeFlow.Model if(!this.IsShareParam && CacheMethodDetails is not null - && targetNode.MethodDetails.AssemblyName.Equals(CacheMethodDetails.AssemblyName) - && targetNode.MethodDetails.MethodName.Equals(CacheMethodDetails.MethodName)) + && targetNode.MethodDetails is not null + && targetNode.MethodDetails.AssemblyName == CacheMethodDetails.AssemblyName + && targetNode.MethodDetails.MethodName == CacheMethodDetails.MethodName) { this.MethodDetails = CacheMethodDetails; } else { - CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份 - targetNode.PropertyChanged += TargetNode_PropertyChanged; - this.MethodDetails = CacheMethodDetails; - /*foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) + if (targetNode.MethodDetails is not null) { - this.SuccessorNodes[ctType] = targetNode.SuccessorNodes[ctType]; - }*/ + CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份 + targetNode.PropertyChanged += TargetNode_PropertyChanged; + this.MethodDetails = CacheMethodDetails; + } + } } @@ -127,11 +125,20 @@ namespace Serein.NodeFlow.Model } if (value) { - CacheMethodDetails = this.MethodDetails; - this.MethodDetails = targetNode.MethodDetails; + CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); + this.MethodDetails = CacheMethodDetails; } else { + + if(targetNode.ControlType == NodeControlType.Script) + { + // 脚本节点入参需不可编辑入参数量、入参名称 + foreach (var item in CacheMethodDetails.ParameterDetailss) + { + item.IsParams = false; + } + } this.MethodDetails = CacheMethodDetails; } @@ -176,52 +183,6 @@ namespace Serein.NodeFlow.Model } - /// - /// 需要调用其它流程图中的某个节点 - /// - /// - /// - /// - public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) - { - if (!UploadTargetNode()) - { - throw new ArgumentNullException(); - } - if (IsShareParam) - { - this.MethodDetails = targetNode.MethodDetails; - } - this.SuccessorNodes = targetNode.SuccessorNodes; - var flowData = await base.ExecutingAsync(context, token); - - if (IsShareParam) - { - // 设置运行时上一节点 - - // 此处代码与SereinFlow.Library.FlowNode.ParameterDetails - // ToMethodArgData()方法中判断流程接口节点分支逻辑耦合 - // 不要轻易修改 - context.AddOrUpdate(targetNode, flowData); - foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) - { - if (this.SuccessorNodes[ctType] == null) continue; - foreach (var node in this.SuccessorNodes[ctType]) - { - if (node.DebugSetting.IsEnable) - { - - context.SetPreviousNode(node, this); - } - } - } - } - - - return flowData; - } - - /// /// 保存全局变量的数据 /// @@ -265,7 +226,61 @@ namespace Serein.NodeFlow.Model CacheMethodDetails = null; } - + + + + /// + /// 需要调用其它流程图中的某个节点 + /// + /// + /// + /// + public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + { + if (!UploadTargetNode()) + { + throw new ArgumentNullException(); + } + if (IsShareParam) + { + this.MethodDetails = targetNode.MethodDetails; + } + this.SuccessorNodes = targetNode.SuccessorNodes; + + FlowResult flowData = await (targetNode.ControlType switch + { + NodeControlType.Script => ((SingleScriptNode)targetNode).ExecutingAsync(this, context, token), + _ => base.ExecutingAsync(context, token) + }); + + + if (IsShareParam) + { + // 设置运行时上一节点 + + // 此处代码与SereinFlow.Library.FlowNode.ParameterDetails + // ToMethodArgData()方法中判断流程接口节点分支逻辑耦合 + // 不要轻易修改 + context.AddOrUpdate(targetNode, flowData); + foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes) + { + if (this.SuccessorNodes[ctType] == null) continue; + foreach (var node in this.SuccessorNodes[ctType]) + { + if (node.DebugSetting.IsEnable) + { + + context.SetPreviousNode(node, this); + } + } + } + } + + + return flowData; + } + + } } \ No newline at end of file diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/SingleScriptNode.cs index e9f6f16..3912158 100644 --- a/NodeFlow/Model/SingleScriptNode.cs +++ b/NodeFlow/Model/SingleScriptNode.cs @@ -33,17 +33,17 @@ namespace Serein.NodeFlow.Model /// public override bool IsBase => true; - private IScriptFlowApi ScriptFlowApi { get; } - + private IScriptFlowApi ScriptFlowApi; private ASTNode mainNode; private SereinScriptInterpreter ScriptInterpreter; + private bool IsScriptChanged = false; + /// /// 构建流程脚本节点 /// /// public SingleScriptNode(IFlowEnvironment environment):base(environment) { - //ScriptFlowApi = environment.IOC.Get(); ScriptFlowApi = new ScriptFlowApi(environment, this); ScriptInterpreter = new SereinScriptInterpreter(); } @@ -64,7 +64,19 @@ namespace Serein.NodeFlow.Model } } + /// + /// 代码改变后 + /// + /// + /// + partial void OnScriptChanged(string value) + { + IsScriptChanged = true; + } + /// + /// 节点创建时 + /// public override void OnCreating() { MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi)); @@ -90,7 +102,7 @@ namespace Serein.NodeFlow.Model InputType = ParameterValueInputType.Input, Items = null, IsParams = true, - Description = "脚本节点入参" + //Description = "脚本节点入参" }; @@ -166,13 +178,35 @@ namespace Serein.NodeFlow.Model /// /// public override async Task ExecutingAsync(IDynamicContext context, CancellationToken token) + { + return await ExecutingAsync(this, context, token); + } + + /// + /// 流程接口提供参数进行调用脚本节点 + /// + /// + /// + /// + /// + public async Task ExecutingAsync(NodeModelBase flowCallNode, IDynamicContext context, CancellationToken token) { if (token.IsCancellationRequested) return new FlowResult(this, context); - var @params = await this.GetParametersAsync(context, token); - if(token.IsCancellationRequested) return new FlowResult(this, context); + var @params = await flowCallNode.GetParametersAsync(context, token); + if (token.IsCancellationRequested) return new FlowResult(this, context); //context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改 - ReloadScript();// 每次都重新解析 + + if (IsScriptChanged) + { + lock (@params) { + if (IsScriptChanged) + { + ReloadScript();// 每次都重新解析 + IsScriptChanged = false; + } + } + } IScriptInvokeContext scriptContext = new ScriptInvokeContext(context); @@ -180,13 +214,12 @@ namespace Serein.NodeFlow.Model { for (int i = 0; i < agrDatas.Length; i++) { - var argName = MethodDetails.ParameterDetailss[i].Name; + var argName = flowCallNode.MethodDetails.ParameterDetailss[i].Name; var argData = agrDatas[i]; scriptContext.SetVarValue(argName, argData); } } - FlowRunCompleteHandler onFlowStop = (e) => { scriptContext.OnExit(); @@ -200,10 +233,8 @@ namespace Serein.NodeFlow.Model var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行 envEvent.OnFlowRunComplete -= onFlowStop; return new FlowResult(this, context, result); - //SereinEnv.WriteLine(InfoType.INFO, "FlowContext Guid : " + context.Guid); } - #region 挂载的方法 public IScriptFlowApi GetFlowApi() diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs index 8f9ab04..708ae32 100644 --- a/Workbench/App.xaml.cs +++ b/Workbench/App.xaml.cs @@ -126,7 +126,7 @@ namespace Serein.Workbench filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf"; filePath = @"F:\临时\project\project.dnf"; filePath = @"F:\TempFile\flow\qrcode\project.dnf"; - filePath = @"F:\TempFile\flow\temp\project.dnf"; + filePath = @"F:\TempFile\flow\temp2\project.dnf"; if (File.Exists(filePath)) { string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 diff --git a/Workbench/Converters/ThumbPositionConverter.cs b/Workbench/Converters/ThumbPositionConverter.cs index 964c9ae..90bf6df 100644 --- a/Workbench/Converters/ThumbPositionConverter.cs +++ b/Workbench/Converters/ThumbPositionConverter.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Data; +using System.Windows.Data; namespace Serein.Workbench.Converters { diff --git a/Workbench/Node/View/FlowCallNodeControl.xaml b/Workbench/Node/View/FlowCallNodeControl.xaml index 6348a38..bc058b5 100644 --- a/Workbench/Node/View/FlowCallNodeControl.xaml +++ b/Workbench/Node/View/FlowCallNodeControl.xaml @@ -66,7 +66,7 @@ - + diff --git a/Workbench/Node/View/FlowCallNodeControl.xaml.cs b/Workbench/Node/View/FlowCallNodeControl.xaml.cs index bf726f2..3764706 100644 --- a/Workbench/Node/View/FlowCallNodeControl.xaml.cs +++ b/Workbench/Node/View/FlowCallNodeControl.xaml.cs @@ -19,12 +19,14 @@ namespace Serein.Workbench.Node.View base.ViewModel = new FlowCallNodeControlViewModel(new SingleFlowCallNode(env)); base.ViewModel.IsEnabledOnView = false; DataContext = base.ViewModel; + base.ViewModel.NodeModel.DisplayName = "[流程接口]"; InitializeComponent(); } public FlowCallNodeControl(FlowCallNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; ViewModel = viewModel; + viewModel.NodeModel.DisplayName = "[流程接口]"; InitializeComponent(); ViewModel.UploadMethodDetailsControl = UploadMethodDetailsControl; diff --git a/Workbench/Node/View/ScriptNodeControl.xaml b/Workbench/Node/View/ScriptNodeControl.xaml index 6cb222d..4117c7b 100644 --- a/Workbench/Node/View/ScriptNodeControl.xaml +++ b/Workbench/Node/View/ScriptNodeControl.xaml @@ -31,7 +31,7 @@ - + @@ -43,6 +43,7 @@ + @@ -73,7 +74,18 @@ + + + + + + + + + + +