diff --git a/Library/DynamicFlow/NodeFlowStarter.cs b/Library/DynamicFlow/NodeFlowStarter.cs index 672e84c..6de1ba9 100644 --- a/Library/DynamicFlow/NodeFlowStarter.cs +++ b/Library/DynamicFlow/NodeFlowStarter.cs @@ -106,7 +106,6 @@ namespace DynamicDemo.Node }).ToArray(); - try { await Task.WhenAll([startNode.ExecuteStack(context),.. tasks]); @@ -138,11 +137,9 @@ namespace DynamicDemo.Node object?[]? parameters = singleFlipFlopNode.GetParameters(context, md); // 调用委托并获取结果 - FlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); - if (flipflopContext == null) { break; @@ -155,7 +152,7 @@ namespace DynamicDemo.Node { singleFlipFlopNode.FlowState = true; singleFlipFlopNode.FlowData = flipflopContext.Data; - var tasks = singleFlipFlopNode.TrueBranch.Select(nextNode => + var tasks = singleFlipFlopNode.SucceedBranch.Select(nextNode => { var context = new DynamicContext(ServiceContainer); nextNode.PreviousNode = singleFlipFlopNode; diff --git a/Library/DynamicFlow/NodeModel/NodeBase.cs b/Library/DynamicFlow/NodeModel/NodeBase.cs index 4e1290d..f519a5f 100644 --- a/Library/DynamicFlow/NodeModel/NodeBase.cs +++ b/Library/DynamicFlow/NodeModel/NodeBase.cs @@ -13,9 +13,22 @@ namespace Serein.DynamicFlow.NodeModel public enum ConnectionType { - IsTrue, - IsFalse, - IsEx, + /// + /// 真分支 + /// + IsSucceed, + /// + /// 假分支 + /// + IsFail, + /// + /// 异常发生分支 + /// + IsError, + /// + /// 上游分支(执行当前节点前会执行一次上游分支) + /// + Upstream, } /// @@ -49,16 +62,19 @@ namespace Serein.DynamicFlow.NodeModel /// /// 下一节点集合(真分支) /// - public List TrueBranch { get; set; } = []; + public List SucceedBranch { get; set; } = []; /// /// 下一节点集合(假分支) /// - public List FalseBranch { get; set; } = []; + public List FailBranch { get; set; } = []; /// /// 异常分支 /// - public List ExBranch { get; set; } = []; - + public List ErrorBranch { get; set; } = []; + /// + /// 上游分支 + /// + public List UpstreamBranch { get; set; } = []; /// /// 当前状态(进入真分支还是假分支,异常分支在异常中确定) @@ -95,16 +111,10 @@ namespace Serein.DynamicFlow.NodeModel object?[]? parameters = GetParameters(context, MethodDetails); if (md.ReturnType == typeof(void)) { - - ((Action)del).Invoke(md.ActingInstance, parameters); - - } else { - - result = ((Func)del).Invoke(md.ActingInstance, parameters); @@ -173,16 +183,20 @@ namespace Serein.DynamicFlow.NodeModel } } } - // context.SetFlowData(result); - // CurrentData = result; } return result; } - - public async Task ExecuteStack(DynamicContext context) + { + await Task.Run(async () => + { + await ExecuteStackTmp(context); + }); + } + + public async Task ExecuteStackTmp(DynamicContext context) { var cts = context.ServiceContainer.Get(); @@ -191,19 +205,23 @@ namespace Serein.DynamicFlow.NodeModel while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环 { - // 从栈中弹出一个节点作为当前节点进行处理 var currentNode = stack.Pop(); - //currentNode.MethodDetails.ActingInstance ??= context.ServiceContainer.Get( - // currentNode.MethodDetails.ActingInstanceType - // ); - if (currentNode.MethodDetails != null) { currentNode.MethodDetails.ActingInstance ??= context.ServiceContainer.Get(MethodDetails.ActingInstanceType); + } // 设置方法执行的对象 + + // 获取上游分支,首先执行一次 + var upstreamNodes = currentNode.UpstreamBranch; + for (int i = upstreamNodes.Count - 1; i >= 0; i--) + { + upstreamNodes[i].PreviousNode = currentNode; + await upstreamNodes[i].ExecuteStack(context); } + if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == DynamicNodeType.Flipflop) { currentNode.FlowData = await currentNode.ExecuteAsync(context); @@ -214,8 +232,8 @@ namespace Serein.DynamicFlow.NodeModel } - var nextNodes = currentNode.FlowState ? currentNode.TrueBranch - : currentNode.FalseBranch; + var nextNodes = currentNode.FlowState ? currentNode.SucceedBranch + : currentNode.FailBranch; // 将下一个节点集合中的所有节点逆序推入栈中 for (int i = nextNodes.Count - 1; i >= 0; i--) @@ -243,6 +261,9 @@ namespace Serein.DynamicFlow.NodeModel var mdEd = md.ExplicitDatas[i]; Type type = mdEd.DataType; + + var f1 = PreviousNode?.FlowData?.GetType(); + var f2 = mdEd.DataType; if (type == typeof(DynamicContext)) { parameters[i] = context; @@ -280,17 +301,19 @@ namespace Serein.DynamicFlow.NodeModel } else { + parameters[i] = ""; - - parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); - - + //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); } } + else if ((f1 != null && f2 != null) && f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName)) + { + parameters[i] = PreviousNode?.FlowData; + } else { + - //var tmpParameter = context.GetFlowData()?.ToString(); var tmpParameter = PreviousNode?.FlowData?.ToString(); if (mdEd.DataType.IsEnum) { diff --git a/SereinWAT/SereinWAT.cs b/SereinWAT/SereinWAT.cs index 72da155..b90ae43 100644 --- a/SereinWAT/SereinWAT.cs +++ b/SereinWAT/SereinWAT.cs @@ -72,6 +72,7 @@ namespace Serein.Module public void Wait([Explicit]int time = 1000) { Thread.Sleep(time); + } [MethodDetail(DynamicNodeType.Action,"启动浏览器")] @@ -145,7 +146,7 @@ namespace Serein.Module [MethodDetail(DynamicNodeType.Action,"定位元素")] - public IWebElement FindElement(string key, [Explicit] ByType byType = ByType.Id, [Explicit] int index = 0) + public IWebElement FindElement([Explicit] string key = "", [Explicit] ByType byType = ByType.XPath, [Explicit] int index = 0) { By by = byType switch { @@ -176,6 +177,8 @@ namespace Serein.Module actions.ContextClick(element).Perform(); break; case ActionType.SendKeys: + element.Click(); + element.Clear(); element.SendKeys(text); break; } diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index 19ae216..b4f8aaf 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -157,13 +157,13 @@ namespace Serein.WorkBench Shutdown(); // 关闭应用程序 } } - //else if(1 == 1) - //{ - // string filePath =@"F:\临时\project\project.dnf"; - // string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 - // FData = JsonConvert.DeserializeObject(content); - // App.FileDataPath = System.IO.Path.GetDirectoryName(filePath); - //} + else if (1 == 1) + { + string filePath = @"F:\临时\project\project.dnf"; + string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 + FData = JsonConvert.DeserializeObject(content); + App.FileDataPath = System.IO.Path.GetDirectoryName(filePath); + } } } diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index cc94ffb..7da1e9e 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -21,6 +21,7 @@ using System.Windows.Shapes; using static Serein.WorkBench.Connection; using DynamicDemo.Node; using Npgsql.Logging; +using System.Threading.Tasks.Dataflow; namespace Serein.WorkBench { @@ -38,8 +39,6 @@ namespace Serein.WorkBench /// public class Connection { - - public required NodeControlBase Start { get; set; } // 起始TextBlock public required NodeControlBase End { get; set; } // 结束TextBlock public required Line Line { get; set; } // 连接的线 @@ -79,9 +78,10 @@ namespace Serein.WorkBench }; // 设置线条的样式 - Line.Stroke = Type == ConnectionType.IsTrue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) - : Type == ConnectionType.IsFalse ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) - : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")); + Line.Stroke = Type == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) + : Type == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) + : Type == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")) + : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")); Line.StrokeDashArray = [dashLength, dashLength]; // 创建新的 Storyboard @@ -102,9 +102,10 @@ namespace Serein.WorkBench if (_animationStoryboard != null) { _animationStoryboard.Stop(); - Line.Stroke = Type == ConnectionType.IsTrue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) - : Type == ConnectionType.IsFalse ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) - : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")); + Line.Stroke = Type == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) + : Type == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) + : Type == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")) + : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")); Line.StrokeDashArray = null; } } @@ -339,8 +340,10 @@ namespace Serein.WorkBench { if (nodeControls.TryGetValue(node.guid, out var fromNode)) { - ConnectNodeControlChildren(fromNode, node.trueNodes, nodeControls, ConnectionType.IsTrue); - ConnectNodeControlChildren(fromNode, node.falseNodes, nodeControls, ConnectionType.IsFalse); + ConnectNodeControlChildren(fromNode, node.trueNodes, nodeControls, ConnectionType.IsSucceed); + ConnectNodeControlChildren(fromNode, node.falseNodes, nodeControls, ConnectionType.IsFail); + //ConnectNodeControlChildren(fromNode, node.errorNodes, nodeControls, ConnectionType.IsError); + ConnectNodeControlChildren(fromNode, node.upstreamNodes, nodeControls, ConnectionType.Upstream); } } } @@ -380,9 +383,10 @@ namespace Serein.WorkBench { IsHitTestVisible = true, - Stroke = connectionType == ConnectionType.IsTrue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) - : connectionType == ConnectionType.IsFalse ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) - : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")), + Stroke = connectionType == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) + : connectionType == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) + : connectionType == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")) + : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")), StrokeThickness = 2, X1 = Canvas.GetLeft(fromNode) + fromNode.ActualWidth / 2, @@ -394,17 +398,21 @@ namespace Serein.WorkBench ConfigureLineContextMenu(line); FlowChartCanvas.Children.Add(line); - if (connectionType == ConnectionType.IsTrue) + if (connectionType == ConnectionType.IsSucceed) { - fromNode.Node.TrueBranch.Add(toNode.Node); + fromNode.Node.SucceedBranch.Add(toNode.Node); } - else if (connectionType == ConnectionType.IsFalse) + else if (connectionType == ConnectionType.IsFail) { - fromNode.Node.FalseBranch.Add(toNode.Node); + fromNode.Node.FailBranch.Add(toNode.Node); } - else if (connectionType == ConnectionType.IsEx) + else if (connectionType == ConnectionType.IsError) { - fromNode.Node.ExBranch.Add(toNode.Node); + fromNode.Node.ErrorBranch.Add(toNode.Node); + } + else if (connectionType == ConnectionType.Upstream) + { + fromNode.Node.UpstreamBranch.Add(toNode.Node); } toNode.Node.PreviousNodes.Add(fromNode.Node); @@ -564,17 +572,20 @@ namespace Serein.WorkBench private void ConfigureContextMenu(NodeControlBase nodeControl) { var contextMenu = new ContextMenu(); - contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) => + + if (nodeControl.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void)) { - if (nodeControl.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void)) + contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) => { DisplayReturnTypeTreeViewer(returnType); - } - })); + })); + } contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => SetIsStartBlock(nodeControl))); contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => DeleteBlock(nodeControl))); - contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsTrue))); - contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFalse))); + contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed))); + contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail))); + contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsError))); + contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionType.Upstream))); nodeControl.ContextMenu = contextMenu; } @@ -1011,10 +1022,6 @@ namespace Serein.WorkBench - - - - /// /// 尝试将节点放置在区域中 /// @@ -1285,9 +1292,10 @@ namespace Serein.WorkBench // 确保起点和终点位置的正确顺序 currentLine = new Line { - Stroke = connectionType == ConnectionType.IsTrue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) - : connectionType == ConnectionType.IsFalse ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) - : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")), + Stroke = connectionType == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) + : connectionType == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) + : connectionType == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")) + : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")), StrokeDashArray = new DoubleCollection([2]), StrokeThickness = 2, X1 = Canvas.GetLeft(startConnectBlock) + startConnectBlock.ActualWidth / 2, @@ -1397,9 +1405,10 @@ namespace Serein.WorkBench Line line = new() { IsHitTestVisible = true, - Stroke = currentConnectionType == ConnectionType.IsTrue ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) - : currentConnectionType == ConnectionType.IsFalse ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) - : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")), + Stroke = currentConnectionType == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")) + : currentConnectionType == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")) + : currentConnectionType == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B")) + : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")), StrokeThickness = 2, X1 = Canvas.GetLeft(startConnectBlock) + startConnectBlock.ActualWidth / 2, Y1 = Canvas.GetTop(startConnectBlock) + startConnectBlock.ActualHeight / 2, @@ -1419,17 +1428,21 @@ namespace Serein.WorkBench FlowChartCanvas.Children.Add(line); - if (currentConnectionType == ConnectionType.IsTrue) + if (currentConnectionType == ConnectionType.IsSucceed) { - startConnectBlock.Node.TrueBranch.Add(targetBlock.Node); + startConnectBlock.Node.SucceedBranch.Add(targetBlock.Node); } - else if (currentConnectionType == ConnectionType.IsFalse) + else if (currentConnectionType == ConnectionType.IsFail) { - startConnectBlock.Node.FalseBranch.Add(targetBlock.Node); + startConnectBlock.Node.FailBranch.Add(targetBlock.Node); } - else if (currentConnectionType == ConnectionType.IsEx) + else if (currentConnectionType == ConnectionType.IsError) { - startConnectBlock.Node.ExBranch.Add(targetBlock.Node); + startConnectBlock.Node.ErrorBranch.Add(targetBlock.Node); + } + else if (currentConnectionType == ConnectionType.Upstream) + { + startConnectBlock.Node.UpstreamBranch.Add(targetBlock.Node); } targetBlock.Node.PreviousNodes.Add(startConnectBlock.Node); // 将当前发起连接的节点,添加到被连接的节点的上一节点队列。(用于回溯) @@ -1597,46 +1610,57 @@ namespace Serein.WorkBench { var startNode = connection.Start.Node; var endNode = connection.End.Node; - bool IsStart = false; + bool IsStartInThisConnection = false; // 要删除的节点(targetNode),在连接关系中是否为起点 // 如果是,则需要从 targetNode 中删除子节点。 // 如果不是,则需要从连接关系中的起始节点删除 targetNode 。 if (startNode.Guid.Equals(targetNode.Guid)) { - IsStart = true; + IsStartInThisConnection = true; } - if (connection.Type == ConnectionType.IsTrue) + if (connection.Type == ConnectionType.IsSucceed) { - if (IsStart) + if (IsStartInThisConnection) { - targetNode.TrueBranch.Remove(endNode); + targetNode.SucceedBranch.Remove(endNode); } else { - startNode.TrueBranch.Remove(targetNode); + startNode.SucceedBranch.Remove(targetNode); } } - else if (connection.Type == ConnectionType.IsFalse) + else if (connection.Type == ConnectionType.IsFail) { - if (IsStart) + if (IsStartInThisConnection) { - targetNode.FalseBranch.Remove(endNode); + targetNode.FailBranch.Remove(endNode); } else { - startNode.FalseBranch.Remove(targetNode); + startNode.FailBranch.Remove(targetNode); } } - else if (connection.Type == ConnectionType.IsEx) + else if (connection.Type == ConnectionType.IsError) { - if (IsStart) + if (IsStartInThisConnection) { - targetNode.ExBranch.Remove(endNode); + targetNode.ErrorBranch.Remove(endNode); } else { - startNode.ExBranch.Remove(targetNode); + startNode.ErrorBranch.Remove(targetNode); + } + } + else if (connection.Type == ConnectionType.Upstream) + { + if (IsStartInThisConnection) + { + targetNode.UpstreamBranch.Remove(endNode); + } + else + { + endNode.UpstreamBranch.Remove(targetNode); } } @@ -1725,17 +1749,17 @@ namespace Serein.WorkBench var StartNode = connectionToRemove.Start.Node; var EndNode = connectionToRemove.End.Node; - if (connectionToRemove.Type == ConnectionType.IsTrue) + if (connectionToRemove.Type == ConnectionType.IsSucceed) { - StartNode.TrueBranch.Remove(EndNode); + StartNode.SucceedBranch.Remove(EndNode); } - else if (connectionToRemove.Type == ConnectionType.IsFalse) + else if (connectionToRemove.Type == ConnectionType.IsFail) { - StartNode.FalseBranch.Remove(EndNode); + StartNode.FailBranch.Remove(EndNode); } - else if (connectionToRemove.Type == ConnectionType.IsEx) + else if (connectionToRemove.Type == ConnectionType.IsError) { - StartNode.ExBranch.Remove(EndNode); + StartNode.ErrorBranch.Remove(EndNode); } @@ -1823,8 +1847,9 @@ namespace Serein.WorkBench { var node = item.Node; Point positionRelativeToParent = item.TranslatePoint(new Point(0, 0), FlowChartCanvas); - var trueNodes = item.Node.TrueBranch.Select(item => item.Guid); // 真分支 - var falseNodes = item.Node.FalseBranch.Select(item => item.Guid);// 加分制 + var trueNodes = item.Node.SucceedBranch.Select(item => item.Guid); // 真分支 + var falseNodes = item.Node.FailBranch.Select(item => item.Guid);// 假分支 + var upstreamNodes = item.Node.UpstreamBranch.Select(item => item.Guid);// 上游分支 // 常规节点的参数信息 List parameterData = []; @@ -1883,6 +1908,7 @@ namespace Serein.WorkBench }, trueNodes = trueNodes.ToArray(), falseNodes = falseNodes.ToArray(), + upstreamNodes = upstreamNodes.ToArray(), parameterData = parameterData.ToArray(), }; diff --git a/WorkBench/SereinOutputFileData.cs b/WorkBench/SereinOutputFileData.cs index 4b9660f..d44f348 100644 --- a/WorkBench/SereinOutputFileData.cs +++ b/WorkBench/SereinOutputFileData.cs @@ -144,6 +144,7 @@ namespace Serein.WorkBench /// public string[] falseNodes { get; set; } + public string[] upstreamNodes { get; set; } @@ -154,7 +155,6 @@ namespace Serein.WorkBench public class Parameterdata { public bool state { get; set; } - public string value { get; set; } public string expression { get; set; } diff --git a/WorkBench/Themes/TypeViewerWindow.xaml.cs b/WorkBench/Themes/TypeViewerWindow.xaml.cs index 0bf16c7..64ac0ba 100644 --- a/WorkBench/Themes/TypeViewerWindow.xaml.cs +++ b/WorkBench/Themes/TypeViewerWindow.xaml.cs @@ -45,7 +45,16 @@ namespace Serein.WorkBench.Themes var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly); foreach (var member in members) { - var memberNode = new TreeViewItem { Header = member.Name }; + TreeViewItem memberNode; + try + { + memberNode = new TreeViewItem { Header = member.Name }; + } + catch + { + return; + } + if (member is PropertyInfo property) { var propertyType = property.PropertyType;