From 66141533b1861a97d6169ecdf1c04ddca5d68f7f Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Mon, 28 Oct 2024 21:52:45 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=B5=81=E7=A8=8B?= =?UTF-8?q?=E8=BF=90=E8=A1=8C=E4=B8=AD=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library.Core/NodeFlow/DynamicContext.cs | 68 +++++- Library.Framework/NodeFlow/DynamicContext.cs | 61 ++++- Library/Api/IDynamicContext.cs | 23 +- Library/Ex/FlipflopException.cs | 6 +- Library/FlowNode/MethodDetails.cs | 2 +- Library/FlowNode/NodeDebugSetting.cs | 2 +- Library/FlowNode/NodeModelBaseData.cs | 213 +----------------- Library/FlowNode/NodeModelBaseFunc.cs | 20 +- Library/FlowNode/ParameterDetails.cs | 2 +- .../SerinExpressionEvaluator.cs | 114 ++++++---- NodeFlow/Env/FlowEnvironment.cs | 7 +- NodeFlow/Env/FlowFunc.cs | 3 +- NodeFlow/FlowStarter.cs | 57 ++--- NodeFlow/Model/CompositeConditionNode.cs | 10 +- NodeFlow/Model/SingleActionNode.cs | 9 + NodeFlow/Model/SingleConditionNode.cs | 28 ++- NodeFlow/Model/SingleExpOpNode.cs | 12 +- NodeFlow/Model/SingleFlipflopNode.cs | 10 +- Serein.BaseNode/Serein.BaseNode.csproj | 30 +++ Serein.BaseNode/SereinBaseNodes.cs | 52 +++++ SereinFlow.sln | 14 +- WorkBench/App.xaml.cs | 4 +- WorkBench/MainWindow.xaml | 7 +- WorkBench/MainWindow.xaml.cs | 4 - WorkBench/Node/View/ConditionNodeControl.xaml | 46 +++- .../Node/View/ConditionNodeControl.xaml.cs | 33 ++- .../ConditionNodeControlViewModel.cs | 54 ++--- 27 files changed, 518 insertions(+), 373 deletions(-) create mode 100644 Serein.BaseNode/Serein.BaseNode.csproj create mode 100644 Serein.BaseNode/SereinBaseNodes.cs diff --git a/Library.Core/NodeFlow/DynamicContext.cs b/Library.Core/NodeFlow/DynamicContext.cs index 436e9f3..081f660 100644 --- a/Library.Core/NodeFlow/DynamicContext.cs +++ b/Library.Core/NodeFlow/DynamicContext.cs @@ -36,25 +36,55 @@ namespace Serein.Library.Core.NodeFlow public ConnectionInvokeType NextOrientation { get; set; } /// - /// 每个上下文分别存放节点的当前数据 + /// 每个流程上下文分别存放节点的当前数据 /// - private readonly ConcurrentDictionary dictNodeFlowData = new ConcurrentDictionary(); + private readonly ConcurrentDictionary dictNodeFlowData = new ConcurrentDictionary(); + + /// + /// 每个流程上下文存储运行时节点的调用关系 + /// + private readonly ConcurrentDictionary dictPreviousNodes = new ConcurrentDictionary(); + + /// + /// 设置运行时上一节点 + /// + /// 当前节点 + /// 上一节点 + public void SetPreviousNode(NodeModelBase currentNodeModel, NodeModelBase PreviousNode) + { + dictPreviousNodes.AddOrUpdate(currentNodeModel, (_)=> PreviousNode, (_,_) => PreviousNode); + } + + /// + /// 获取当前节点的运行时上一节点 + /// + /// + /// + public NodeModelBase GetPreviousNode(NodeModelBase currentNodeModel) + { + if (dictPreviousNodes.TryGetValue(currentNodeModel, out var node)) + { + return node; + } + else + { + return null; + } + } + /// /// 获取节点当前数据 /// - /// + /// 节点 /// public object? GetFlowData(string nodeGuid) { - if (string.IsNullOrEmpty(nodeGuid)) - { - return null; - } - if(dictNodeFlowData.TryGetValue(nodeGuid,out var data)) + if(dictNodeFlowData.TryGetValue(nodeGuid, out var data)) { return data; } + else { return null; } @@ -63,14 +93,32 @@ namespace Serein.Library.Core.NodeFlow /// /// 添加或更新当前节点数据 /// - /// 节点Guid + /// 节点 /// 新的数据 - public void AddOrUpdate(string nodeGuid,object? flowData) + public void AddOrUpdate(string nodeGuid, object? flowData) { // this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData); this.dictNodeFlowData.AddOrUpdate(nodeGuid, _ => flowData, (_, _) => flowData); } + /// + /// 上一节点数据透传到下一节点 + /// + /// + public object? TransmissionData(NodeModelBase nodeModel) + { + if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点 + { + if (dictNodeFlowData.TryGetValue(previousNode.Guid, out var data)) // 其次获取上一节点的数据 + { + return data; + //AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中 + } + } + return null; + } + + /// /// 结束流程 /// diff --git a/Library.Framework/NodeFlow/DynamicContext.cs b/Library.Framework/NodeFlow/DynamicContext.cs index 2e1ad96..b3770ca 100644 --- a/Library.Framework/NodeFlow/DynamicContext.cs +++ b/Library.Framework/NodeFlow/DynamicContext.cs @@ -18,7 +18,6 @@ namespace Serein.Library.Framework.NodeFlow RunState = RunState.Running; } - /// /// 运行环境 @@ -40,10 +39,38 @@ namespace Serein.Library.Framework.NodeFlow /// private readonly ConcurrentDictionary dictNodeFlowData = new ConcurrentDictionary(); + private readonly ConcurrentDictionary dictPreviousNodes = new ConcurrentDictionary(); + + /// + /// 设置运行时上一节点 + /// + /// 当前节点 + /// 上一节点 + public void SetPreviousNode(NodeModelBase currentNodeModel, NodeModelBase PreviousNode) + { + dictPreviousNodes.AddOrUpdate(currentNodeModel, (n1) => PreviousNode, (n1, n2) => PreviousNode); + } + + /// + /// 获取当前节点的运行时上一节点 + /// + /// + /// + public NodeModelBase GetPreviousNode(NodeModelBase currentNodeModel) + { + if (dictPreviousNodes.TryGetValue(currentNodeModel, out var node)) + { + return node; + } + else + { + return null; + } + } + /// /// 获取节点当前数据 /// - /// /// public object GetFlowData(string nodeGuid) { @@ -51,6 +78,7 @@ namespace Serein.Library.Framework.NodeFlow { return data; } + else { return null; } @@ -59,14 +87,32 @@ namespace Serein.Library.Framework.NodeFlow /// /// 添加或更新当前节点数据 /// - /// 节点Guid + /// 节点 /// 新的数据 public void AddOrUpdate(string nodeGuid, object flowData) { // this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData); - this.dictNodeFlowData[nodeGuid] = flowData; + this.dictNodeFlowData.AddOrUpdate(nodeGuid, n1 => flowData, (n1, n2)=> flowData); } + /// + /// 上一节点数据透传到下一节点 + /// + /// + public object TransmissionData(NodeModelBase nodeModel) + { + if (dictPreviousNodes.TryGetValue(nodeModel, out var previousNode)) // 首先获取当前节点的上一节点 + { + if (dictNodeFlowData.TryGetValue(previousNode.Guid, out var data)) // 其次获取上一节点的数据 + { + return data; + //AddOrUpdate(nodeModel.Guid, data); // 然后作为当前节点的数据记录在上下文中 + } + } + return null; + } + + /// /// 结束流程 /// @@ -74,7 +120,11 @@ namespace Serein.Library.Framework.NodeFlow { foreach (var nodeObj in dictNodeFlowData.Values) { - if (nodeObj != null) + if (nodeObj is null) + { + continue; + } + else { if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable) { @@ -85,7 +135,6 @@ namespace Serein.Library.Framework.NodeFlow this.dictNodeFlowData?.Clear(); RunState = RunState.Completion; } - // public NodeRunCts NodeRunCts { get; set; } // public ISereinIOC SereinIoc { get; } //public Task CreateTimingTask(Action action, int time = 100, int count = -1) diff --git a/Library/Api/IDynamicContext.cs b/Library/Api/IDynamicContext.cs index f097dc4..54b3f81 100644 --- a/Library/Api/IDynamicContext.cs +++ b/Library/Api/IDynamicContext.cs @@ -21,10 +21,24 @@ namespace Serein.Library.Api RunState RunState { get; } /// - /// 下一个要执行的节点 + /// 下一个要执行的节点类别 /// ConnectionInvokeType NextOrientation { get; set; } + /// + /// 设置节点的运行时上一节点,用以多线程中隔开不同流程的数据 + /// + /// 当前节点 + /// 运行时上一节点 + void SetPreviousNode(NodeModelBase currentNodeModel, NodeModelBase PreviousNode); + + /// + /// 获取当前节点的运行时上一节点,用以流程中获取数据 + /// + /// + /// + NodeModelBase GetPreviousNode(NodeModelBase currentNodeModel); + /// /// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取 /// @@ -32,6 +46,13 @@ namespace Serein.Library.Api /// object GetFlowData(string nodeGuid); + /// + /// 上一节点数据透传到下一节点 + /// + /// + object TransmissionData(NodeModelBase nodeModel); + + /// /// 添加或更新当前节点的数据 /// diff --git a/Library/Ex/FlipflopException.cs b/Library/Ex/FlipflopException.cs index 48cdbc3..960c68d 100644 --- a/Library/Ex/FlipflopException.cs +++ b/Library/Ex/FlipflopException.cs @@ -13,11 +13,11 @@ namespace Serein.Library /// /// 取消触发器当前所在分支的继续执行 /// - Branch, + CancelBranch, /// /// 取消整个触发器流程的再次执行(用于停止全局触发器) /// - Flow, + CancelFlow, } /// /// 是否已取消 @@ -27,7 +27,7 @@ namespace Serein.Library /// 取消类型 /// public CancelClass Type { get; } - public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.Branch) :base(message) + public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.CancelBranch) :base(message) { IsCancel = isCancel; Type = clsss; diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs index 67b26e4..53157c5 100644 --- a/Library/FlowNode/MethodDetails.cs +++ b/Library/FlowNode/MethodDetails.cs @@ -160,7 +160,7 @@ namespace Serein.Library StringBuilder sb = new StringBuilder(); sb.AppendLine($"方法别名:{this.MethodAnotherName}"); sb.AppendLine($"方法名称:{this.MethodName}"); - sb.AppendLine($"需要实例:{this.ActingInstanceType.FullName}"); + sb.AppendLine($"需要实例:{this.ActingInstanceType?.FullName}"); sb.AppendLine($""); sb.AppendLine($"入参参数信息:"); foreach (var arg in this.ParameterDetailss) diff --git a/Library/FlowNode/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs index b3e7cac..7df571d 100644 --- a/Library/FlowNode/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -42,7 +42,7 @@ namespace Serein.Library /// /// 中断级别,暂时停止继续执行后继分支。 /// - [PropertyInfo(IsNotification = true)] // CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);" + [PropertyInfo(IsNotification = true, CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);")] // CustomCode = "NodeModel?.Env?.SetNodeInterruptAsync(NodeModel?.Guid, value);" private bool _isInterrupt = false; /// diff --git a/Library/FlowNode/NodeModelBaseData.cs b/Library/FlowNode/NodeModelBaseData.cs index 545e89b..c999403 100644 --- a/Library/FlowNode/NodeModelBaseData.cs +++ b/Library/FlowNode/NodeModelBaseData.cs @@ -66,8 +66,8 @@ namespace Serein.Library /// /// 运行时的上一节点 /// - [PropertyInfo] - private NodeModelBase _previousNode ; + //[PropertyInfo] + //private NodeModelBase _previousNode ; /// /// 当前节点执行完毕后需要执行的下一个分支的类别 @@ -86,6 +86,11 @@ namespace Serein.Library public abstract partial class NodeModelBase : IDynamicFlowNode { + /// + /// 加载完成后调用的方法 + /// + public abstract void OnLoading(); + public NodeModelBase(IFlowEnvironment environment) { PreviousNodes = new Dictionary>(); @@ -100,7 +105,6 @@ namespace Serein.Library } - /// /// 不同分支的父节点 /// @@ -111,210 +115,7 @@ namespace Serein.Library /// public Dictionary> SuccessorNodes { get; } - - /// - /// 控制FlowData在同一时间只会被同一个线程更改。 - /// - //private readonly ReaderWriterLockSlim _flowDataLock = new ReaderWriterLockSlim(); - //private object _flowData; - ///// - ///// 当前传递数据(执行了节点对应的方法,才会存在值)。 - ///// - //protected object FlowData - //{ - // get - // { - // _flowDataLock.EnterReadLock(); - // try - // { - // return _flowData; - // } - // finally - // { - // _flowDataLock.ExitReadLock(); - // } - // } - // set - // { - // _flowDataLock.EnterWriteLock(); - // try - // { - // _flowData = value; - // } - // finally - // { - // _flowDataLock.ExitWriteLock(); - // } - // } - //} } } - - /* - /// - /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 - /// - public abstract partial class NodeModelBase : IDynamicFlowNode - { - - /// - /// 节点保留对环境的引用,因为需要在属性更改时通知 - /// - public IFlowEnvironment Env { get; } - - /// - /// 在画布中的位置 - /// - public PositionOfUI Position { get; set; } - - /// - /// 附加的调试功能 - /// - public NodeDebugSetting DebugSetting { get; set; } - - /// - /// 描述节点对应的控件类型 - /// - public NodeControlType ControlType { get; set; } - - /// - /// 方法描述。不包含Method与委托,需要通过MethodName从环境中获取委托进行调用。 - /// - public MethodDetails MethodDetails { get; set; } - - /// - /// 标识节点对象全局唯一 - /// - public string Guid { get; set; } - - /// - /// 显示名称 - /// - public string DisplayName { get; set; } = string.Empty; - - /// - /// 是否为起点控件 - /// - public bool IsStart { get; set; } - - /// - /// 运行时的上一节点 - /// - public NodeModelBase PreviousNode { get; set; } - - /// - /// 当前节点执行完毕后需要执行的下一个分支的类别 - /// - public ConnectionType NextOrientation { get; set; } = ConnectionType.None; - - /// - /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) - /// - public Exception RuningException { get; set; } = null; - - - }*/ - - - - /// - /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 - /// - //public class NodeModelBaseBuilder - //{ - // public NodeModelBaseBuilder(NodeModelBase builder) - // { - // this.ControlType = builder.ControlType; - // this.MethodDetails = builder.MethodDetails; - // this.Guid = builder.Guid; - // this.DisplayName = builder.DisplayName; - // this.IsStart = builder.IsStart; - // this.PreviousNode = builder.PreviousNode; - // this.PreviousNodes = builder.PreviousNodes; - // this.SucceedBranch = builder.SucceedBranch; - // this.FailBranch = builder.FailBranch; - // this.ErrorBranch = builder.ErrorBranch; - // this.UpstreamBranch = builder.UpstreamBranch; - // this.FlowState = builder.FlowState; - // this.RuningException = builder.RuningException; - // this.FlowData = builder.FlowData; - // } - - - - // /// - // /// 节点对应的控件类型 - // /// - // public NodeControlType ControlType { get; } - - // /// - // /// 方法描述,对应DLL的方法 - // /// - // public MethodDetails MethodDetails { get; } - - // /// - // /// 节点guid - // /// - // public string Guid { get; } - - // /// - // /// 显示名称 - // /// - // public string DisplayName { get;} - - // /// - // /// 是否为起点控件 - // /// - // public bool IsStart { get; } - - // /// - // /// 运行时的上一节点 - // /// - // public NodeModelBase? PreviousNode { get; } - - // /// - // /// 上一节点集合 - // /// - // public List PreviousNodes { get; } = []; - - // /// - // /// 下一节点集合(真分支) - // /// - // public List SucceedBranch { get; } = []; - - // /// - // /// 下一节点集合(假分支) - // /// - // public List FailBranch { get; } = []; - - // /// - // /// 异常分支 - // /// - // public List ErrorBranch { get; } = []; - - // /// - // /// 上游分支 - // /// - // public List UpstreamBranch { get; } = []; - - // /// - // /// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定) - // /// - // public FlowStateType FlowState { get; set; } = FlowStateType.None; - - // /// - // /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) - // /// - // public Exception RuningException { get; set; } = null; - - // /// - // /// 当前传递数据(执行了节点对应的方法,才会存在值) - // /// - // public object? FlowData { get; set; } = null; - //} - - - - diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index 32facce..93799b8 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -173,7 +173,7 @@ namespace Serein.Library var cancelType = await upstreamNode.DebugSetting.GetInterruptTask(); await Console.Out.WriteLineAsync($"[{upstreamNode.MethodDetails?.MethodName}]中断已{cancelType},开始执行后继分支"); } - upstreamNode.PreviousNode = currentNode; + context.SetPreviousNode(upstreamNode, currentNode); await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支 if (context.NextOrientation == ConnectionInvokeType.IsError) { @@ -186,6 +186,7 @@ namespace Serein.Library } // 上游分支执行完成,才执行当前节点 if (IsBradk(context, flowCts)) break; // 退出执行 + context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态 object newFlowData = await currentNode.ExecutingAsync(context); if (IsBradk(context, flowCts)) break; // 退出执行 @@ -204,7 +205,7 @@ namespace Serein.Library // 筛选出启用的节点的节点 if (nextNodes[i].DebugSetting.IsEnable) { - nextNodes[i].PreviousNode = currentNode; + context.SetPreviousNode(nextNodes[i], currentNode); stack.Push(nextNodes[i]); } } @@ -248,7 +249,10 @@ namespace Serein.Library { object[] args = await GetParametersAsync(context, this, md); var result = await dd.InvokeAsync(md.ActingInstance, args); - context.NextOrientation = ConnectionInvokeType.IsSucceed; + if(context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置 + { + context.NextOrientation = ConnectionInvokeType.IsSucceed; + } return result; } catch (Exception ex) @@ -342,7 +346,10 @@ namespace Serein.Library { if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) { - var previousFlowData = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据 + var previousNode = context.GetPreviousNode(nodeModel); + var previousFlowData = context.GetFlowData(previousNode.Guid); // 当前传递的数据 + + // 执行表达式从上一节点获取对象 inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, previousFlowData, out _); } @@ -356,7 +363,8 @@ namespace Serein.Library { if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData) { - inputParameter = context.GetFlowData(nodeModel?.PreviousNode?.Guid); // 当前传递的数据 + var previousNode = context.GetPreviousNode(nodeModel); + inputParameter = context.GetFlowData(previousNode.Guid); // 当前传递的数据 } else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData) { @@ -509,6 +517,7 @@ namespace Serein.Library public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object newData = null) { string guid = nodeModel.Guid; + context.AddOrUpdate(guid, newData); // 上下文中更新数据 if (newData is null) { } @@ -517,7 +526,6 @@ namespace Serein.Library await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象 await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点 //nodeModel.FlowData = newData; // 替换数据 - context.AddOrUpdate(guid, newData); // 上下文中更新数据 } } diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 30fa744..42f8b7c 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -174,7 +174,7 @@ namespace Serein.Library { if(_convertor is null) { - return $"[{this.Index}] {this.Name} : {this.DataType.FullName}"; + return $"[{this.Index}] {this.Name} : {this.DataType?.FullName}"; } else { diff --git a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs index e583501..9f73aa0 100644 --- a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs +++ b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs @@ -2,7 +2,10 @@ using System; using System.Collections.Generic; using System.Data; +using System.Diagnostics; using System.Linq; +using System.Reflection; +using System.Threading.Tasks; namespace Serein.Library.Utils.SereinExpression { @@ -152,53 +155,88 @@ namespace Serein.Library.Utils.SereinExpression throw new ArgumentException($"Invalid array syntax for member {member}"); } - // 提取数组索引 - var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1); - if (!int.TryParse(indexStr, out int index)) + var targetType = target?.GetType(); // 目标对象的类型 + if(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { - throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}"); - } - // 获取数组或集合对象 - var arrayProperty = target?.GetType().GetProperty(arrayName); - if (arrayProperty is null) - { - var arrayField = target?.GetType().GetField(arrayName); - if (arrayField is null) + var typetmp = target.GetType().FullName; + // 目标是键值对 + var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1); + var method = targetType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance); + if(method != null) { - throw new ArgumentException($"Member {arrayName} not found on target."); + var result = method.Invoke(target, new object[] { indexStr }); + if(result != null) + { + return result; + } + } + + //var dict = target as Dictionary; + ////var dict = (Dictionary)target; + //var temp = dict[indexStr]; + ////if (target is Dictionary dict) + ////{ + //// var temp = dict[indexStr]; + ////} + //var TMP2= target.GetType().GetEnumValues(); + + } + else + { + + #region 表达式处理集合对象 + // 获取数组或集合对象 + var arrayProperty = target?.GetType().GetProperty(arrayName); + if (arrayProperty is null) + { + var arrayField = target?.GetType().GetField(arrayName); + if (arrayField is null) + { + throw new ArgumentException($"Member {arrayName} not found on target."); + } + else + { + target = arrayField.GetValue(target); + } } else { - target = arrayField.GetValue(target); + target = arrayProperty.GetValue(target); } - } - else - { - target = arrayProperty.GetValue(target); + + + // 提取数组索引 + var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1); + if (!int.TryParse(indexStr, out int index)) + { + throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}"); + } + // 访问数组或集合中的指定索引 + if (target is Array array) + { + if (index < 0 || index >= array.Length) + { + throw new ArgumentException($"Index {index} out of bounds for array {arrayName}"); + } + target = array.GetValue(index); + } + else if (target is IList list) + { + if (index < 0 || index >= list.Count) + { + throw new ArgumentException($"Index {index} out of bounds for list {arrayName}"); + } + target = list[index]; + } + else + { + throw new ArgumentException($"Member {arrayName} is not an array or list."); + } + #endregion } - // 访问数组或集合中的指定索引 - if (target is Array array) - { - if (index < 0 || index >= array.Length) - { - throw new ArgumentException($"Index {index} out of bounds for array {arrayName}"); - } - target = array.GetValue(index); - } - else if (target is IList list) - { - if (index < 0 || index >= list.Count) - { - throw new ArgumentException($"Index {index} out of bounds for list {arrayName}"); - } - target = list[index]; - } - else - { - throw new ArgumentException($"Member {arrayName} is not an array or list."); - } + } else { diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 618b6b5..76e449b 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -1438,8 +1438,7 @@ namespace Serein.NodeFlow.Env try { Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件 - Type[] types = assembly.GetTypes(); // 获取程序集中的所有类型 - + List types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型 Dictionary> autoRegisterTypes = new Dictionary>(); foreach (Type type in types) { @@ -1720,10 +1719,6 @@ namespace Serein.NodeFlow.Env if (!string.IsNullOrEmpty(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid)) { - //if(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType == connectionArgSourceType) - //{ - // return ; - //} await RemoteConnectAsync(fromNode,toNode,argIndex); // 已经存在连接,将其移除 } toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid; diff --git a/NodeFlow/Env/FlowFunc.cs b/NodeFlow/Env/FlowFunc.cs index 1152329..867300d 100644 --- a/NodeFlow/Env/FlowFunc.cs +++ b/NodeFlow/Env/FlowFunc.cs @@ -55,8 +55,7 @@ namespace Serein.NodeFlow.Env var md = methodDetails.CloneOfNode(nodeModel.Env, nodeModel); nodeModel.DisplayName = md.MethodAnotherName; nodeModel.MethodDetails = md; - - + nodeModel.OnLoading(); return nodeModel; } diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 31db32a..076da5a 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -169,21 +169,6 @@ namespace Serein.NodeFlow #endregion - #region 检查并修正初始化、加载时、退出时方法作用的对象,保证后续不会报错(已注释) - //foreach (var md in initMethods) // 初始化 - //{ - // md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType); - //} - //foreach (var md in loadingMethods) // 加载 - //{ - // md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType); - //} - //foreach (var md in exitMethods) // 初始化 - //{ - // md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType); - //} - #endregion - #region 执行初始化,绑定IOC容器,再执行加载时 if (autoRegisterTypes.TryGetValue(RegisterSequence.FlowInit, out var flowInitTypes)) @@ -356,7 +341,9 @@ namespace Serein.NodeFlow /// 流程运行全局环境 /// 需要全局监听信号的触发器 /// - private async Task FlipflopExecuteAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode, CancellationTokenSource cts) + private async Task FlipflopExecuteAsync(IFlowEnvironment env, + SingleFlipflopNode singleFlipFlopNode, + CancellationTokenSource cts) { if(_flipFlopCts is null) { @@ -369,34 +356,38 @@ namespace Serein.NodeFlow try { var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task + context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData); await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据 if (context.NextOrientation == ConnectionInvokeType.None) { continue; } - var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation]; - for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--) - { - // 筛选出启用的节点 - if (!nextNodes[i].DebugSetting.IsEnable) + _ = Task.Run(async () => { + var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation]; + for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--) { - continue; - } + // 筛选出启用的节点 + if (!nextNodes[i].DebugSetting.IsEnable) + { + continue ; + } - nextNodes[i].PreviousNode = singleFlipFlopNode; - if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 - { - var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); - await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); + context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); + if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 + { + var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); + await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); + } + await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 + context.Exit(); } - await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 - } - + }); + } catch (FlipflopException ex) { await Console.Out.WriteLineAsync($"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message); - if (ex.Type == FlipflopException.CancelClass.Flow) + if (ex.Type == FlipflopException.CancelClass.CancelFlow) { break; } @@ -407,7 +398,7 @@ namespace Serein.NodeFlow } finally { - context.Exit(); + } } diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index eff7428..f3295a0 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -29,6 +29,13 @@ namespace Serein.NodeFlow.Model } + /// + /// 加载完成后调用的方法 + /// + public override void OnLoading() + { + Console.WriteLine("CompositeConditionNode 暂未实现 OnLoading"); + } public void AddNode(SingleConditionNode node) { @@ -59,7 +66,8 @@ namespace Serein.NodeFlow.Model } } - return Task.FromResult(context.GetFlowData(PreviousNode.Guid)); // 条件区域透传上一节点的数据 + //var previousNode = context.GetPreviousNode() + return Task.FromResult(context.TransmissionData(this)); // 条件区域透传上一节点的数据 } diff --git a/NodeFlow/Model/SingleActionNode.cs b/NodeFlow/Model/SingleActionNode.cs index 5602462..a8a25df 100644 --- a/NodeFlow/Model/SingleActionNode.cs +++ b/NodeFlow/Model/SingleActionNode.cs @@ -13,6 +13,15 @@ namespace Serein.NodeFlow.Model { } + + /// + /// 加载完成后调用的方法 + /// + public override void OnLoading() + { + Console.WriteLine("SingleActionNode 暂未实现 OnLoading"); + } + public override ParameterData[] GetParameterdatas() { if (base.MethodDetails.ParameterDetailss.Length > 0) diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index 4c21f21..e25ca1e 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -39,8 +39,34 @@ namespace Serein.NodeFlow.Model this.IsCustomData = false; this.CustomData = null; this.Expression = "PASS"; + } + /// + /// 加载完成后调用的方法 + /// + public override void OnLoading() + { + var pd = new ParameterDetails + { + Index = 0, + Name = "Exp", + DataType = typeof(object), + ExplicitType = typeof(object), + IsExplicitData = false, + DataValue = string.Empty, + ArgDataSourceNodeGuid = string.Empty, + ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, + NodeModel = this, + Convertor = null, + ExplicitTypeName = "Value", + Items = Array.Empty(), + }; + + this.MethodDetails.ParameterDetailss = new ParameterDetails[] { pd }; + } + + /// /// 重写节点的方法执行 /// @@ -50,7 +76,7 @@ namespace Serein.NodeFlow.Model { // 接收上一节点参数or自定义参数内容 object? parameter; - object? result = context.GetFlowData(PreviousNode.Guid); // 条件节点透传上一节点的数据 + object? result = context.TransmissionData(this); // 条件节点透传上一节点的数据 if (IsCustomData) // 是否使用自定义参数 { // 表达式获取上一节点数据 diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index da97270..1f33e43 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -28,10 +28,18 @@ namespace Serein.NodeFlow.Model } - //public override async Task Executing(IDynamicContext context) + /// + /// 加载完成后调用的方法 + /// + public override void OnLoading() + { + Console.WriteLine("SingleExpOpNode 暂未实现 OnLoading"); + } + + public override Task ExecutingAsync(IDynamicContext context) { - var data = context.GetFlowData(PreviousNode.Guid); // 表达式节点使用上一节点数据 + var data = context.TransmissionData(this); // 表达式节点使用上一节点数据 try { diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs index 899ab38..9025da1 100644 --- a/NodeFlow/Model/SingleFlipflopNode.cs +++ b/NodeFlow/Model/SingleFlipflopNode.cs @@ -16,6 +16,14 @@ namespace Serein.NodeFlow.Model } + /// + /// 加载完成后调用的方法 + /// + public override void OnLoading() + { + Console.WriteLine("SingleFlipflopNode 暂未实现 OnLoading"); + } + /// /// 执行触发器进行等待触发 @@ -56,7 +64,7 @@ namespace Serein.NodeFlow.Model } catch (FlipflopException ex) { - if(ex.Type == FlipflopException.CancelClass.Flow) + if(ex.Type == FlipflopException.CancelClass.CancelFlow) { throw; } diff --git a/Serein.BaseNode/Serein.BaseNode.csproj b/Serein.BaseNode/Serein.BaseNode.csproj new file mode 100644 index 0000000..a1a36c4 --- /dev/null +++ b/Serein.BaseNode/Serein.BaseNode.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + 1.0.0 + + D:\Project\C#\DynamicControl\SereinFlow\.Output + True + SereinFow + 基础节点 + README.md + https://github.com/fhhyyp/serein-flow + MIT + True + + + + + + + + True + \ + + + True + \ + + + diff --git a/Serein.BaseNode/SereinBaseNodes.cs b/Serein.BaseNode/SereinBaseNodes.cs new file mode 100644 index 0000000..9d54f6a --- /dev/null +++ b/Serein.BaseNode/SereinBaseNodes.cs @@ -0,0 +1,52 @@ +using Serein.Library; +using Serein.Library.Api; +using Serein.Library.Utils.SereinExpression; + +namespace Serein.BaseNode +{ + + public enum ExpType + { + Get, + Set + } + [DynamicFlow(Name ="基础节点")] + internal class SereinBaseNodes + { + [NodeAction(NodeType.Action,"条件节点")] + private bool SereinConditionNode(IDynamicContext context, + object targetObject, + string exp = "ISPASS") + { + var isPass = SereinConditionParser.To(targetObject, exp); + context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail; + return isPass; + } + + + + + [NodeAction(NodeType.Action, "表达式节点")] + private object SereinExpNode(IDynamicContext context, + object targetObject, + string exp) + { + + exp = "@" + exp; + var newData = SerinExpressionEvaluator.Evaluate(exp, targetObject, out bool isChange); + object result; + if (isChange || exp.StartsWith("@GET",System.StringComparison.OrdinalIgnoreCase)) + { + result = newData; + } + else + { + result = targetObject; + } + context.NextOrientation = ConnectionInvokeType.IsSucceed; + return result; + } + + + } +} diff --git a/SereinFlow.sln b/SereinFlow.sln index 92ac60f..2ff6fb1 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -20,11 +20,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library", "Library\S EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Net462DllTest", "Net462DllTest\Net462DllTest.csproj", "{E40EE629-1A38-4011-88E3-9AD036869987}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Extend.RemoteControl", "Extend.FlowRemoteManagement\Serein.Extend.RemoteControl.csproj", "{3E568C47-74C6-4C28-9D43-C9BA29008DB7}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.FlowStartTool", "FlowStartTool\Serein.FlowStartTool.csproj", "{38D0FA92-5139-4616-A41E-8186AA4C1532}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Library.NodeGenerator", "Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.NodeGenerator", "Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj", "{5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.BaseNode", "Serein.BaseNode\Serein.BaseNode.csproj", "{9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,10 +56,6 @@ Global {E40EE629-1A38-4011-88E3-9AD036869987}.Debug|Any CPU.Build.0 = Debug|Any CPU {E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.ActiveCfg = Release|Any CPU {E40EE629-1A38-4011-88E3-9AD036869987}.Release|Any CPU.Build.0 = Release|Any CPU - {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E568C47-74C6-4C28-9D43-C9BA29008DB7}.Release|Any CPU.Build.0 = Release|Any CPU {38D0FA92-5139-4616-A41E-8186AA4C1532}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {38D0FA92-5139-4616-A41E-8186AA4C1532}.Debug|Any CPU.Build.0 = Debug|Any CPU {38D0FA92-5139-4616-A41E-8186AA4C1532}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -68,6 +64,10 @@ Global {5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F7DE0B2-A5D3-492D-AC6C-F0C39EBEF365}.Release|Any CPU.Build.0 = Release|Any CPU + {9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9E7CEECB-EC9F-4D5F-8A04-49865B6DEC99}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index bcc31fe..1d0d606 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -13,10 +13,10 @@ namespace Serein.Workbench void LoadLocalProject() { #if DEBUG - if (1 == 1) + if (1 == 11) { string filePath; - filePath = @"F:\临时\project\linux\project.dnf"; + filePath = @"F:\临时\project\linux\http\project.dnf"; string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 App.FlowProjectData = JsonConvert.DeserializeObject(content); App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index de96d14..7427049 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -89,15 +89,16 @@ - - + + - + + diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index e212519..e30bf29 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -511,10 +511,6 @@ namespace Serein.Workbench #endregion #endregion } - - - - } /// diff --git a/WorkBench/Node/View/ConditionNodeControl.xaml b/WorkBench/Node/View/ConditionNodeControl.xaml index 9c67c62..9bbc7c9 100644 --- a/WorkBench/Node/View/ConditionNodeControl.xaml +++ b/WorkBench/Node/View/ConditionNodeControl.xaml @@ -17,7 +17,7 @@ - + @@ -25,16 +25,39 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + - + + +