修改了logwindows输出,避免高频输出时卡死。修改了流程运行上下文,使节点具备终止分支运行的能力。

This commit is contained in:
fengjiayi
2024-10-14 17:29:28 +08:00
parent f76f09da94
commit 4338554384
93 changed files with 4640 additions and 541 deletions

View File

@@ -94,6 +94,36 @@ namespace Serein.NodeFlow.Base
#region
/// <summary>
/// 是否应该退出执行
/// </summary>
/// <param name="context"></param>
/// <param name="flowCts"></param>
/// <returns></returns>
public static bool IsBradk(IDynamicContext context, CancellationTokenSource? flowCts)
{
// 上下文不再执行
if(context.RunState == RunState.Completion)
{
return true;
}
// 不存在全局触发器时,流程运行状态被设置为完成,退出执行,用于打断无限循环分支。
if (flowCts is null && context.Env.FlowState == RunState.Completion)
{
return true;
}
// 如果存在全局触发器,且触发器的执行任务已经被取消时,退出执行。
if (flowCts is not null)
{
if (flowCts.IsCancellationRequested)
return true;
}
return false;
}
/// <summary>
/// 开始执行
/// </summary>
@@ -107,24 +137,18 @@ namespace Serein.NodeFlow.Base
bool hasFlipflow = flowCts != null;
while (stack.Count > 0) // 循环中直到栈为空才会退出循环
{
if (hasFlipflow && flowCts is not null)
{
if (flowCts.IsCancellationRequested)
break;
}
await Task.Delay(0);
// 从栈中弹出一个节点作为当前节点进行处理
var currentNode = stack.Pop();
#region
// 筛选出上游分支
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].Where(
node => node.DebugSetting.IsEnable
).ToArray();
// 执行上游分支
foreach (var upstreamNode in upstreamNodes)
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].ToArray();
for (int index = 0; index < upstreamNodes.Length; index++)
{
if (upstreamNode.DebugSetting.IsEnable)
NodeModelBase? upstreamNode = upstreamNodes[index];
if (upstreamNode is not null && upstreamNode.DebugSetting.IsEnable)
{
if (upstreamNode.DebugSetting.InterruptClass != InterruptClass.None) // 执行触发前
{
@@ -142,14 +166,11 @@ namespace Serein.NodeFlow.Base
}
}
}
if (IsBradk(context, flowCts)) break; // 退出执行
// 上游分支执行完成,才执行当前节点
object? newFlowData = await currentNode.ExecutingAsync(context);
if (hasFlipflow && (flowCts is null || flowCts.IsCancellationRequested || currentNode.NextOrientation == ConnectionType.None))
{
// 不再执行
break;
}
if (IsBradk(context, flowCts)) break; // 退出执行
await RefreshFlowDataAndExpInterrupt(context, currentNode, newFlowData); // 执行当前节点后刷新数据
#endregion
@@ -169,6 +190,7 @@ namespace Serein.NodeFlow.Base
stack.Push(nextNodes[i]);
}
}
#endregion
}
@@ -342,7 +364,10 @@ namespace Serein.NodeFlow.Base
/// <summary>
/// 更新节点数据,并检查监视表达式是否生效
/// </summary>
/// <param name="newData"></param>
/// <param name="context">上下文</param>
/// <param name="nodeModel">节点Moel</param>
/// <param name="newData">新的数据</param>
/// <returns></returns>
public static async Task RefreshFlowDataAndExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object? newData = null)
{
string guid = nodeModel.Guid;
@@ -351,6 +376,7 @@ namespace Serein.NodeFlow.Base
await MonitorObjExpInterrupt(context, nodeModel, newData, 0); // 首先监视对象
await MonitorObjExpInterrupt(context, nodeModel, newData, 1); // 然后监视节点
nodeModel.FlowData = newData; // 替换数据
context.AddOrUpdate(guid, nodeModel); // 上下文中更新数据
}
}

View File

@@ -150,6 +150,14 @@ namespace Serein.NodeFlow
#endregion
#region
/// <summary>
/// 如果没有全局触发器,且没有循环分支,流程执行完成后自动为 Completion 。
/// </summary>
public RunState FlowState { get; set; } = RunState.NoStart;
/// <summary>
/// 如果全局触发器还在运行,则为 Running 。
/// </summary>
public RunState FlipFlopState { get; set; } = RunState.NoStart;
/// <summary>
/// 环境名称
@@ -166,8 +174,18 @@ namespace Serein.NodeFlow
/// </summary>
public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; }
/// <summary>
/// <para>单例模式IOC容器内部维护了一个实例字典默认使用类型的FullName作为Key如果以“接口-实现类”的方式注册那么将使用接口类型的FullName作为Key。</para>
/// <para>当某个类型注册绑定成功后,将不会因为其它地方尝试注册相同类型的行为导致类型被重新创建。</para>
/// </summary>
public ISereinIOC IOC { get => this; }
/// <summary>
/// 描述所有DLL中NodeAction特性的方法的原始副本
/// </summary>
public Dictionary<NodeLibrary, List<MethodDetails>> MethodDetailss { get; } = [];
#endregion
#region
@@ -185,10 +203,7 @@ namespace Serein.NodeFlow
/// </summary>
public List<NodeLibrary> NodeLibrarys { get; } = [];
/// <summary>
/// 描述所有DLL中NodeAction特性的方法的原始副本
/// </summary>
public Dictionary<NodeLibrary, List<MethodDetails>> MethodDetailss { get; } = [];
/// <summary>
/// 环境加载的节点集合
@@ -283,7 +298,7 @@ namespace Serein.NodeFlow
await flowStarter.RunAsync(this, nodes, AutoRegisterTypes, initMethods, loadMethods, exitMethods);
if (flowStarter?.FlipFlopState == RunState.NoStart)
if (this.FlipFlopState == RunState.Completion)
{
this.Exit(); // 未运行触发器时,才会调用结束方法
}
@@ -296,7 +311,7 @@ namespace Serein.NodeFlow
{
return;
}
if (flowStarter.FlowState == RunState.Running || flowStarter.FlipFlopState == RunState.Running)
if (this.FlowState == RunState.Running || this.FlipFlopState == RunState.Running)
{
NodeModelBase? nodeModel = GuidToModel(startNodeGuid);
if (nodeModel is null || nodeModel is SingleFlipflopNode)
@@ -580,7 +595,7 @@ namespace Serein.NodeFlow
/// </summary>
/// <param name="nodeGuid"></param>
/// <exception cref="NotImplementedException"></exception>
public void RemoteNode(string nodeGuid)
public void RemoveNode(string nodeGuid)
{
var remoteNode = GuidToModel(nodeGuid);
if (remoteNode is null) return;
@@ -653,7 +668,7 @@ namespace Serein.NodeFlow
/// <param name="toNodeGuid">目标节点Guid</param>
/// <param name="connectionType">连接关系</param>
/// <exception cref="NotImplementedException"></exception>
public void RemoteConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
public void RemoveConnect(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
{
// 获取起始节点与目标节点
var fromNode = GuidToModel(fromNodeGuid);
@@ -853,7 +868,7 @@ namespace Serein.NodeFlow
if (nodeModel is null) return;
if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
{
if (flowStarter.FlowState != RunState.Completion
if (this.FlowState != RunState.Completion
&& flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点
{
_ = flowStarter.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中

View File

@@ -17,48 +17,28 @@ using static Serein.Library.Utils.ChannelFlowInterrupt;
namespace Serein.NodeFlow
{
/// <summary>
/// 流程启动器
/// </summary>
/// <param name="serviceContainer"></param>
/// <param name="methodDetails"></param>
public class FlowStarter
{
/// <summary>
/// 全局触发器CTS
/// </summary>
public const string FlipFlopCtsName = "<>.FlowFlipFlopCts";
/// <summary>
/// 流程运行CTS
/// </summary>
public const string FlowRungCtsName = "<>.FlowRungCtsName";
public FlowStarter()
{
}
/// <summary>
/// 流程运行状态
/// </summary>
public enum RunState
{
/// <summary>
/// 等待开始
/// </summary>
NoStart,
/// <summary>
/// 正在运行
/// </summary>
Running,
/// <summary>
/// 运行完成
/// </summary>
Completion,
}
/// <summary>
/// 起点流程运行状态
/// </summary>
public RunState FlowState { get; private set; } = RunState.NoStart;
/// <summary>
/// 全局触发器运行状态
/// </summary>
public RunState FlipFlopState { get; private set; } = RunState.NoStart;
/// <summary>
/// 控制触发器
@@ -115,11 +95,11 @@ namespace Serein.NodeFlow
List<MethodDetails> loadingMethods,
List<MethodDetails> exitMethods)
{
FlowState = RunState.Running; // 开始运行
env.FlowState = RunState.Running; // 开始运行
NodeModelBase? startNode = nodes.FirstOrDefault(node => node.IsStart);
if (startNode is null) {
FlowState = RunState.Completion; // 不存在起点,退出流程
env.FlowState = RunState.Completion; // 不存在起点,退出流程
return;
}
@@ -281,8 +261,8 @@ namespace Serein.NodeFlow
_flipFlopCts?.Cancel();
_flipFlopCts?.Dispose();
}
FlowState = RunState.Completion;
FlipFlopState = RunState.Completion;
env.FlowState = RunState.Completion;
env.FlipFlopState = RunState.Completion;
};
#endregion
@@ -294,7 +274,7 @@ namespace Serein.NodeFlow
if (flipflopNodes.Count > 0)
{
FlipFlopState = RunState.Running;
env.FlipFlopState = RunState.Running;
// 如果存在需要启动的触发器,则开始启动
_flipFlopCts = new CancellationTokenSource();
env.IOC.CustomRegisterInstance(FlipFlopCtsName, _flipFlopCts,false);
@@ -308,7 +288,7 @@ namespace Serein.NodeFlow
}
await startNode.StartFlowAsync(Context); // 开始运行时从起始节点开始运行
// 等待结束
if(FlipFlopState == RunState.Running && _flipFlopCts is not null)
if(env.FlipFlopState == RunState.Running && _flipFlopCts is not null)
{
while (!_flipFlopCts.IsCancellationRequested)
{
@@ -322,7 +302,7 @@ namespace Serein.NodeFlow
}
finally
{
FlowState = RunState.Completion;
env.FlowState = RunState.Completion;
}
#endregion
}

View File

@@ -37,7 +37,7 @@ namespace Serein.NodeFlow.Model
break;
}
}
return Task.FromResult( PreviousNode?.GetFlowData());
return Task.FromResult(PreviousNode?.GetFlowData()); // 条件区域透传上一节点的数据
}

View File

@@ -32,35 +32,33 @@ namespace Serein.NodeFlow.Model
public override Task<object?> ExecutingAsync(IDynamicContext context)
{
// 接收上一节点参数or自定义参数内容
object? result;
if (IsCustomData)
object? parameter;
object? result = PreviousNode?.GetFlowData(); // 条件节点透传上一节点的数据
if (IsCustomData) // 是否使用自定义参数
{
result = CustomData;
// 表达式获取上一节点数据
var getObjExp = CustomData?.ToString();
if (!string.IsNullOrEmpty(getObjExp) && getObjExp.Length >= 4 && getObjExp[..4].Equals("@get", StringComparison.CurrentCultureIgnoreCase))
{
parameter = result;
if (parameter is not null)
{
parameter = SerinExpressionEvaluator.Evaluate(getObjExp, parameter, out _);
}
}
else
{
parameter = CustomData;
}
}
else
{
result = PreviousNode?.GetFlowData();
parameter = result;
}
try
{
var getObjExp = CustomData?.ToString();
if (IsCustomData && !string.IsNullOrEmpty(getObjExp) && getObjExp.Length >= 4)
{
var ExpOpOption = getObjExp[..4];
if(ExpOpOption.ToLower() == "@get")
{
result = PreviousNode?.GetFlowData();
if (result is not null)
{
result = SerinExpressionEvaluator.Evaluate(getObjExp, result, out _);
}
}
}
var isPass = SereinConditionParser.To(result, Expression);
var isPass = SereinConditionParser.To(parameter, Expression);
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
}
catch (Exception ex)

View File

@@ -21,7 +21,7 @@ namespace Serein.NodeFlow.Model
//public override async Task<object?> Executing(IDynamicContext context)
public override Task<object?> ExecutingAsync(IDynamicContext context)
{
var data = PreviousNode?.GetFlowData();
var data = PreviousNode?.GetFlowData(); // 表达式节点使用上一节点数据
try
{

View File

@@ -1,189 +0,0 @@
using Serein.Library.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow
{
/* /// <summary>
/// 输出文件
/// </summary>
public class SereinOutputFileData
{
/// <summary>
/// 基础
/// </summary>
public Basic basic { get; set; }
/// <summary>
/// 依赖的DLL
/// </summary>
public Library[] library { get; set; }
/// <summary>
/// 起始节点GUID
/// </summary>
public string startNode { get; set; }
/// <summary>
/// 节点信息集合
/// </summary>
public NodeInfo[] nodes { get; set; }
/// <summary>
/// 区域集合
/// </summary>
public Region[] regions { get; set; }
}
/// <summary>
/// 基础
/// </summary>
public class Basic
{
/// <summary>
/// 画布
/// </summary>
public FlowCanvas canvas { get; set; }
/// <summary>
/// 版本
/// </summary>
public string versions { get; set; }
// 预览位置
// 缩放比例
}
/// <summary>
/// 画布
/// </summary>
public class FlowCanvas
{
/// <summary>
/// 宽度
/// </summary>
public float width { get; set; }
/// <summary>
/// 高度
/// </summary>
public float lenght { get; set; }
}
/// <summary>
/// DLL
/// </summary>
public class Library
{
/// <summary>
/// DLL名称
/// </summary>
public string name { get; set; }
/// <summary>
/// 路径
/// </summary>
public string path { get; set; }
/// <summary>
/// 提示
/// </summary>
public string tips { get; set; }
}
/// <summary>
/// 节点
/// </summary>
public class NodeInfo
{
/// <summary>
/// GUID
/// </summary>
public string guid { get; set; }
/// <summary>
/// 名称
/// </summary>
public string name { get; set; }
/// <summary>
/// 显示标签
/// </summary>
public string label { get; set; }
/// <summary>
/// 类型
/// </summary>
public string type { get; set; }
/// <summary>
/// 于画布中的位置
/// </summary>
public Position position { get; set; }
/// <summary>
/// 真分支节点GUID
/// </summary>
public string[] trueNodes { get; set; }
/// <summary>
/// 假分支节点
/// </summary>
public string[] falseNodes { get; set; }
public string[] upstreamNodes { get; set; }
public Parameterdata[] parameterData { get; set; }
}
public class Parameterdata
{
public bool state { get; set; }
public string value { get; set; }
public string expression { get; set; }
}
/// <summary>
/// 节点于画布中的位置
/// </summary>
public class Position
{
public float x { get; set; }
public float y { get; set; }
}
/// <summary>
/// 区域
/// </summary>
public class Region
{
public string guid { get; set; }
public NodeInfo[] childNodes { get; set; }
}*/
}

View File

@@ -530,13 +530,9 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
"=" => ValueTypeConditionResolver<T>.Operator.Equal,
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
"" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
">=" or "≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
"<=" or "≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
@@ -553,10 +549,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
return operatorStr switch
{
"is" => BoolConditionResolver.Operator.Is,
"==" => BoolConditionResolver.Operator.Is,
"equals" => BoolConditionResolver.Operator.Is,
//"isFalse" => BoolConditionNode.Operator.IsFalse,
"is" or "==" or "equals" => BoolConditionResolver.Operator.Is,
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
};
}
@@ -569,21 +562,16 @@ namespace Serein.NodeFlow.Tool.SereinExpression
/// <exception cref="ArgumentException"></exception>
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
{
return operatorStr switch
return operatorStr.ToLower() switch
{
"c" => StringConditionResolver.Operator.Contains,
"nc" => StringConditionResolver.Operator.DoesNotContain,
"sw" => StringConditionResolver.Operator.StartsWith,
"ew" => StringConditionResolver.Operator.EndsWith,
"c" or "contains" => StringConditionResolver.Operator.Contains,
"nc" or "doesnotcontain" => StringConditionResolver.Operator.DoesNotContain,
"sw" or "startswith" => StringConditionResolver.Operator.StartsWith,
"ew" or "endswith" => StringConditionResolver.Operator.EndsWith,
"==" or "equals" => StringConditionResolver.Operator.Equal,
"!=" or "notequals" => StringConditionResolver.Operator.NotEqual,
"contains" => StringConditionResolver.Operator.Contains,
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
"equals" => StringConditionResolver.Operator.Equal,
"==" => StringConditionResolver.Operator.Equal,
"notEquals" => StringConditionResolver.Operator.NotEqual,
"!=" => StringConditionResolver.Operator.NotEqual,
"startsWith" => StringConditionResolver.Operator.StartsWith,
"endsWith" => StringConditionResolver.Operator.EndsWith,
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
};
}