diff --git a/Library/Api/IFlowContext.cs b/Library/Api/IFlowContext.cs
index 116d95d..fe35e20 100644
--- a/Library/Api/IFlowContext.cs
+++ b/Library/Api/IFlowContext.cs
@@ -2,6 +2,9 @@
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
+using System.Data.Common;
+using System.Globalization;
+using System.Linq;
using System.Threading.Tasks;
namespace Serein.Library.Api
@@ -11,6 +14,12 @@ namespace Serein.Library.Api
///
public interface IFlowContext
{
+ ///
+ /// 是否记录流程信息
+ ///
+ bool IsRecordInvokeInfo { get; set; }
+
+
///
/// 标识流程
///
@@ -39,6 +48,19 @@ namespace Serein.Library.Api
Exception ExceptionOfRuning { get; set; }
+ ///
+ /// 获取当前流程上下文的所有节点调用信息,包含每个节点的执行时间、调用类型、执行状态等。
+ ///
+ ///
+ List GetAllInvokeInfos();
+
+
+ ///
+ /// 新增当前流程上下文的节点调用信息。
+ ///
+ FlowInvokeInfo NewInvokeInfo(IFlowNode previousNode, IFlowNode theNode, FlowInvokeInfo.InvokeType invokeType);
+
+
///
/// 获取节点的运行时参数数据
///
@@ -107,5 +129,176 @@ namespace Serein.Library.Api
/// 用以提前结束当前上下文流程的运行
///
void Exit();
- }
+ }
+
+
+ ///
+ /// 流程调用信息,记录每个节点的调用信息,包括执行时间、调用类型、执行状态等。
+ ///
+ public sealed class FlowInvokeInfo
+ {
+ ///
+ /// 调用类型枚举,指示节点的调用方式。
+ ///
+ public enum InvokeType
+ {
+ ///
+ /// 初始化。
+ ///
+ None = -1,
+ ///
+ /// 上游分支调用,指向上游流程的节点。
+ ///
+ Upstream = 0,
+ ///
+ /// 真分支调用,指示节点执行成功后的分支。
+ ///
+ IsSucceed = 1,
+ ///
+ /// 假分支调用,指示节点执行失败后的分支。
+ ///
+ IsFail = 2,
+ ///
+ /// 异常发生分支调用,指示节点执行过程中发生异常后的分支。
+ ///
+ IsError = 3,
+ ///
+ /// 参数来源调用,标明此次调用出自其它节点需要参数时的执行。
+ ///
+ ArgSource = 4,
+ }
+
+ ///
+ /// 运行状态枚举,指示节点的执行结果。
+ ///
+ public enum RunState
+ {
+ ///
+ /// 初始化
+ ///
+ None = -1,
+ ///
+ /// 正在运行,指示节点正在执行中。
+ ///
+ Running = 0,
+ ///
+ /// 执行成功,指示节点执行完成且结果正常。
+ ///
+ Succeed = 1,
+ ///
+ /// 执行失败,指示节点执行完成但结果异常或不符合预期。
+ ///
+ Failed = 2,
+ ///
+ /// 执行异常,指示节点在执行过程中发生了未处理的异常。
+ ///
+ Error = 3,
+ }
+
+ ///
+ /// 流程上下文标识符,唯一标识一个流程上下文
+ ///
+ public string ContextGuid { get; set; }
+
+ ///
+ /// 节点执行信息标识符,唯一标识一个节点执行信息
+ ///
+ public long Id { get; set; }
+
+ ///
+ /// 上一节点的唯一标识符,指向流程图中的上一个节点
+ ///
+ public string PreviousNodeGuid { get; set; }
+
+ ///
+ /// 节点唯一标识符,指向流程图中的节点
+ ///
+ public string NodeGuid { get; set; }
+
+ ///
+ /// 执行时间,记录节点执行的时间戳
+ ///
+ public DateTime StateTime { get; } = DateTime.Now;
+
+ ///
+ /// 节点调用类型,指示节点的调用方式
+ ///
+ public InvokeType Type { get; set; }
+
+
+
+ ///
+ /// 节点方法名称,指示节点执行的方法
+ ///
+ public string Method { get; set; }
+
+ ///
+ /// 节点执行状态,指示节点的执行结果
+ ///
+ public RunState State { get; set; }
+
+ ///
+ /// 耗时,记录节点执行的耗时(单位:毫秒)
+ ///
+ public TimeSpan TS { get; private set; } = TimeSpan.Zero;
+
+ ///
+ /// 节点参数,存储节点执行时的参数信息
+ ///
+ public string[] Parameters { get; private set; }
+
+ ///
+ /// 节点执行结果,存储节点执行后的结果信息
+ ///
+ public string Result { get; private set; }
+
+ public void UploadState(RunState runState)
+ {
+ State = runState;
+ TS = DateTime.Now - StateTime;
+ }
+ public void UploadResultValue(object value = null)
+ {
+ if(value is null)
+ {
+ Result = string.Empty;
+ }
+ else
+ {
+ Result = value.ToString();
+ }
+ }
+ public void UploadParameters(object[] values = null)
+ {
+ if (values is null)
+ {
+ Parameters = [];
+
+ }
+ else
+ {
+ Parameters = values.Select(v => v.ToString()).ToArray();
+ }
+ }
+
+ public override string ToString()
+ {
+ return $"[{State}]{TS.TotalSeconds:0.000}ms : {Result}";
+ }
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
}
+
+
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index fe5e0ba..d8eaa04 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -414,10 +414,11 @@ namespace Serein.Library.Api
///
public class NodeTakeOutEventArgs : FlowEventArgs
{
- public NodeTakeOutEventArgs(string canvasGuid, string nodeGuid)
+ public NodeTakeOutEventArgs(string canvasGuid, string containerNodeGuid, string nodeGuid)
{
CanvasGuid = canvasGuid;
NodeGuid = nodeGuid;
+ ContainerNodeGuid = containerNodeGuid;
}
public string CanvasGuid { get; }
@@ -426,6 +427,11 @@ namespace Serein.Library.Api
/// 需要取出的节点Guid
///
public string NodeGuid { get; private set; }
+
+ ///
+ /// 容器节点Guid
+ ///
+ public string ContainerNodeGuid { get; private set; }
}
diff --git a/Library/Api/INodeContainer.cs b/Library/Api/INodeContainer.cs
index cdd8198..ebdc398 100644
--- a/Library/Api/INodeContainer.cs
+++ b/Library/Api/INodeContainer.cs
@@ -11,6 +11,8 @@ namespace Serein.Library.Api
///
public interface INodeContainer
{
+
+ string Guid { get; }
///
/// 放置一个节点
///
diff --git a/Library/Extension/FlowModelExtension.cs b/Library/Extension/FlowModelExtension.cs
index 3320261..4defd34 100644
--- a/Library/Extension/FlowModelExtension.cs
+++ b/Library/Extension/FlowModelExtension.cs
@@ -208,7 +208,9 @@ namespace Serein.Library
Stack stack = new Stack();
HashSet processedNodes = new HashSet(); // 用于记录已处理上游节点的节点
stack.Push(nodeModel);
-
+#nullable enable
+ IFlowNode? previousNode = null;
+ IFlowNode? currentNode = null;
while (true)
{
if (token.IsCancellationRequested)
@@ -218,9 +220,30 @@ namespace Serein.Library
#region 执行相关
// 从栈中弹出一个节点作为当前节点进行处理
- var currentNode = stack.Pop();
+ previousNode = currentNode;
+ currentNode = stack.Pop();
+
+
+
+ #region 新增调用信息
+ FlowInvokeInfo? invokeInfo = null;
+ var isRecordInvokeInfo = context.IsRecordInvokeInfo;
+ if (!isRecordInvokeInfo) goto Label_NotRecordInvoke;
+
+ FlowInvokeInfo.InvokeType invokeType = context.NextOrientation switch
+ {
+ ConnectionInvokeType.IsSucceed => FlowInvokeInfo.InvokeType.IsSucceed,
+ ConnectionInvokeType.IsFail => FlowInvokeInfo.InvokeType.IsFail,
+ ConnectionInvokeType.IsError => FlowInvokeInfo.InvokeType.IsError,
+ ConnectionInvokeType.Upstream => FlowInvokeInfo.InvokeType.Upstream,
+ _ => FlowInvokeInfo.InvokeType.None
+ };
+ invokeInfo = context.NewInvokeInfo(previousNode, currentNode, invokeType);
+ #endregion
+
+Label_NotRecordInvoke:
context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态
- FlowResult flowResult = null;
+ FlowResult flowResult = null;
try
{
flowResult = await currentNode.ExecutingAsync(context, token);
@@ -239,6 +262,22 @@ namespace Serein.Library
}
#endregion
+ #region 更新调用信息
+ var state = context.NextOrientation switch
+ {
+ ConnectionInvokeType.IsFail => FlowInvokeInfo.RunState.Failed,
+ ConnectionInvokeType.IsError => FlowInvokeInfo.RunState.Error,
+ _ => FlowInvokeInfo.RunState.Succeed
+ };
+ if (isRecordInvokeInfo)
+ {
+ invokeInfo.UploadState(state);
+ invokeInfo.UploadResultValue(flowResult.Value);
+ }
+
+
+ #endregion
+
#region 执行完成时更新栈
context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
@@ -289,10 +328,18 @@ namespace Serein.Library
#endregion
#if DEBUG
- await Task.Delay(1);
+ //await Task.Delay(1);
#endif
}
}
+
+ ///
+ /// 获取所有参数
+ ///
+ ///
+ ///
+ ///
+ ///
public static async Task