mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-21 07:16:34 +08:00
流程上下文添加了调用信息记录
This commit is contained in:
@@ -2,6 +2,9 @@
|
|||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Data.Common;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Serein.Library.Api
|
namespace Serein.Library.Api
|
||||||
@@ -11,6 +14,12 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IFlowContext
|
public interface IFlowContext
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否记录流程信息
|
||||||
|
/// </summary>
|
||||||
|
bool IsRecordInvokeInfo { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 标识流程
|
/// 标识流程
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,6 +48,19 @@ namespace Serein.Library.Api
|
|||||||
Exception ExceptionOfRuning { get; set; }
|
Exception ExceptionOfRuning { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取当前流程上下文的所有节点调用信息,包含每个节点的执行时间、调用类型、执行状态等。
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
List<FlowInvokeInfo> GetAllInvokeInfos();
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新增当前流程上下文的节点调用信息。
|
||||||
|
/// </summary>
|
||||||
|
FlowInvokeInfo NewInvokeInfo(IFlowNode previousNode, IFlowNode theNode, FlowInvokeInfo.InvokeType invokeType);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取节点的运行时参数数据
|
/// 获取节点的运行时参数数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -107,5 +129,176 @@ namespace Serein.Library.Api
|
|||||||
/// 用以提前结束当前上下文流程的运行
|
/// 用以提前结束当前上下文流程的运行
|
||||||
/// </summary>
|
/// </summary>
|
||||||
void Exit();
|
void Exit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 流程调用信息,记录每个节点的调用信息,包括执行时间、调用类型、执行状态等。
|
||||||
|
/// </summary>
|
||||||
|
public sealed class FlowInvokeInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 调用类型枚举,指示节点的调用方式。
|
||||||
|
/// </summary>
|
||||||
|
public enum InvokeType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化。
|
||||||
|
/// </summary>
|
||||||
|
None = -1,
|
||||||
|
/// <summary>
|
||||||
|
/// 上游分支调用,指向上游流程的节点。
|
||||||
|
/// </summary>
|
||||||
|
Upstream = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 真分支调用,指示节点执行成功后的分支。
|
||||||
|
/// </summary>
|
||||||
|
IsSucceed = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 假分支调用,指示节点执行失败后的分支。
|
||||||
|
/// </summary>
|
||||||
|
IsFail = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 异常发生分支调用,指示节点执行过程中发生异常后的分支。
|
||||||
|
/// </summary>
|
||||||
|
IsError = 3,
|
||||||
|
/// <summary>
|
||||||
|
/// 参数来源调用,标明此次调用出自其它节点需要参数时的执行。
|
||||||
|
/// </summary>
|
||||||
|
ArgSource = 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 运行状态枚举,指示节点的执行结果。
|
||||||
|
/// </summary>
|
||||||
|
public enum RunState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化
|
||||||
|
/// </summary>
|
||||||
|
None = -1,
|
||||||
|
/// <summary>
|
||||||
|
/// 正在运行,指示节点正在执行中。
|
||||||
|
/// </summary>
|
||||||
|
Running = 0,
|
||||||
|
/// <summary>
|
||||||
|
/// 执行成功,指示节点执行完成且结果正常。
|
||||||
|
/// </summary>
|
||||||
|
Succeed = 1,
|
||||||
|
/// <summary>
|
||||||
|
/// 执行失败,指示节点执行完成但结果异常或不符合预期。
|
||||||
|
/// </summary>
|
||||||
|
Failed = 2,
|
||||||
|
/// <summary>
|
||||||
|
/// 执行异常,指示节点在执行过程中发生了未处理的异常。
|
||||||
|
/// </summary>
|
||||||
|
Error = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 流程上下文标识符,唯一标识一个流程上下文
|
||||||
|
/// </summary>
|
||||||
|
public string ContextGuid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点执行信息标识符,唯一标识一个节点执行信息
|
||||||
|
/// </summary>
|
||||||
|
public long Id { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 上一节点的唯一标识符,指向流程图中的上一个节点
|
||||||
|
/// </summary>
|
||||||
|
public string PreviousNodeGuid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点唯一标识符,指向流程图中的节点
|
||||||
|
/// </summary>
|
||||||
|
public string NodeGuid { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行时间,记录节点执行的时间戳
|
||||||
|
/// </summary>
|
||||||
|
public DateTime StateTime { get; } = DateTime.Now;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点调用类型,指示节点的调用方式
|
||||||
|
/// </summary>
|
||||||
|
public InvokeType Type { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点方法名称,指示节点执行的方法
|
||||||
|
/// </summary>
|
||||||
|
public string Method { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点执行状态,指示节点的执行结果
|
||||||
|
/// </summary>
|
||||||
|
public RunState State { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 耗时,记录节点执行的耗时(单位:毫秒)
|
||||||
|
/// </summary>
|
||||||
|
public TimeSpan TS { get; private set; } = TimeSpan.Zero;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点参数,存储节点执行时的参数信息
|
||||||
|
/// </summary>
|
||||||
|
public string[] Parameters { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点执行结果,存储节点执行后的结果信息
|
||||||
|
/// </summary>
|
||||||
|
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}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -414,10 +414,11 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class NodeTakeOutEventArgs : FlowEventArgs
|
public class NodeTakeOutEventArgs : FlowEventArgs
|
||||||
{
|
{
|
||||||
public NodeTakeOutEventArgs(string canvasGuid, string nodeGuid)
|
public NodeTakeOutEventArgs(string canvasGuid, string containerNodeGuid, string nodeGuid)
|
||||||
{
|
{
|
||||||
CanvasGuid = canvasGuid;
|
CanvasGuid = canvasGuid;
|
||||||
NodeGuid = nodeGuid;
|
NodeGuid = nodeGuid;
|
||||||
|
ContainerNodeGuid = containerNodeGuid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string CanvasGuid { get; }
|
public string CanvasGuid { get; }
|
||||||
@@ -426,6 +427,11 @@ namespace Serein.Library.Api
|
|||||||
/// 需要取出的节点Guid
|
/// 需要取出的节点Guid
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string NodeGuid { get; private set; }
|
public string NodeGuid { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 容器节点Guid
|
||||||
|
/// </summary>
|
||||||
|
public string ContainerNodeGuid { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,8 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface INodeContainer
|
public interface INodeContainer
|
||||||
{
|
{
|
||||||
|
|
||||||
|
string Guid { get; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 放置一个节点
|
/// 放置一个节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -208,7 +208,9 @@ namespace Serein.Library
|
|||||||
Stack<IFlowNode> stack = new Stack<IFlowNode>();
|
Stack<IFlowNode> stack = new Stack<IFlowNode>();
|
||||||
HashSet<IFlowNode> processedNodes = new HashSet<IFlowNode>(); // 用于记录已处理上游节点的节点
|
HashSet<IFlowNode> processedNodes = new HashSet<IFlowNode>(); // 用于记录已处理上游节点的节点
|
||||||
stack.Push(nodeModel);
|
stack.Push(nodeModel);
|
||||||
|
#nullable enable
|
||||||
|
IFlowNode? previousNode = null;
|
||||||
|
IFlowNode? currentNode = null;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
if (token.IsCancellationRequested)
|
if (token.IsCancellationRequested)
|
||||||
@@ -218,9 +220,30 @@ namespace Serein.Library
|
|||||||
|
|
||||||
#region 执行相关
|
#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; // 重置上下文状态
|
context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态
|
||||||
FlowResult flowResult = null;
|
FlowResult flowResult = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
flowResult = await currentNode.ExecutingAsync(context, token);
|
flowResult = await currentNode.ExecutingAsync(context, token);
|
||||||
@@ -239,6 +262,22 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
#endregion
|
#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 执行完成时更新栈
|
#region 执行完成时更新栈
|
||||||
context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
|
context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
|
||||||
|
|
||||||
@@ -289,10 +328,18 @@ namespace Serein.Library
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
await Task.Delay(1);
|
//await Task.Delay(1);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取所有参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="token"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public static async Task<object[]> GetParametersAsync(this IFlowNode nodeModel, IFlowContext context, CancellationToken token)
|
public static async Task<object[]> GetParametersAsync(this IFlowNode nodeModel, IFlowContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
var md = nodeModel.MethodDetails;
|
var md = nodeModel.MethodDetails;
|
||||||
@@ -437,6 +484,7 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
var context = new FlowContext(flowCallNode.Env);
|
var context = new FlowContext(flowCallNode.Env);
|
||||||
|
|
||||||
for (int index = 0; index < pds.Length; index++)
|
for (int index = 0; index < pds.Length; index++)
|
||||||
{
|
{
|
||||||
ParameterDetails pd = pds[index];
|
ParameterDetails pd = pds[index];
|
||||||
|
|||||||
@@ -1,8 +1,13 @@
|
|||||||
using Serein.Library.Api;
|
using Microsoft.VisualBasic;
|
||||||
|
using Serein.Library.Api;
|
||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Serein.Library
|
namespace Serein.Library
|
||||||
{
|
{
|
||||||
@@ -24,6 +29,11 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
private string _guid = global::System.Guid.NewGuid().ToString();
|
private string _guid = global::System.Guid.NewGuid().ToString();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否记录流程调用信息
|
||||||
|
/// </summary>
|
||||||
|
public bool IsRecordInvokeInfo { get; set; } = true;
|
||||||
string IFlowContext.Guid => _guid;
|
string IFlowContext.Guid => _guid;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -36,7 +46,6 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public RunState RunState { get; set; } = RunState.NoStart;
|
public RunState RunState { get; set; } = RunState.NoStart;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前节点执行完成后,设置该属性,让运行环境判断接下来要执行哪个分支的节点。
|
/// 当前节点执行完成后,设置该属性,让运行环境判断接下来要执行哪个分支的节点。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,27 +75,43 @@ namespace Serein.Library
|
|||||||
/// 记录节点的运行时参数数据
|
/// 记录节点的运行时参数数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<int, object>> dictNodeParams = new ConcurrentDictionary<string, ConcurrentDictionary<int, object>>();
|
private readonly ConcurrentDictionary<string, ConcurrentDictionary<int, object>> dictNodeParams = new ConcurrentDictionary<string, ConcurrentDictionary<int, object>>();
|
||||||
/*
|
|
||||||
/// <summary>
|
|
||||||
/// 每个流程上下文分别存放节点的当前数据
|
|
||||||
/// </summary>
|
|
||||||
private readonly ConcurrentDictionary<IFlowNode, FlowResult> dictNodeFlowData = new ConcurrentDictionary<IFlowNode, FlowResult>();
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 每个流程上下文存储运行时节点的调用关系
|
/// 记录流程调用信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<IFlowNode, IFlowNode> dictPreviousNodes = new ConcurrentDictionary<IFlowNode, IFlowNode>();
|
//private Dictionary<long, IFlowNode> flowInvokeNodes = new Dictionary<long, IFlowNode>();
|
||||||
|
private Dictionary<long, FlowInvokeInfo> flowInvokeInfos = new Dictionary<long, FlowInvokeInfo>();
|
||||||
|
private static long _idCounter = 0;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 记录忽略处理的流程
|
/// 在执行方法之前,获取新的调用信息
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<IFlowNode, bool> dictIgnoreNodeFlow = new ConcurrentDictionary<IFlowNode, bool>();
|
/// <param name="previousNode">上一个节点</param>
|
||||||
|
/// <param name="theNode">执行节点</param>
|
||||||
|
public FlowInvokeInfo NewInvokeInfo(IFlowNode previousNode, IFlowNode theNode, FlowInvokeInfo.InvokeType invokeType)
|
||||||
|
{
|
||||||
|
//Interlocked
|
||||||
|
var id = Interlocked.Increment(ref _idCounter);
|
||||||
|
|
||||||
/// <summary>
|
FlowInvokeInfo flowInvokeInfo = new FlowInvokeInfo
|
||||||
/// 记录节点的运行时参数数据
|
{
|
||||||
/// </summary>
|
ContextGuid = this._guid,
|
||||||
private readonly ConcurrentDictionary<IFlowNode, ConcurrentDictionary<int, object>> dictNodeParams = new ConcurrentDictionary<IFlowNode, ConcurrentDictionary<int, object>>();*/
|
Id = id,
|
||||||
|
PreviousNodeGuid = previousNode?.Guid,
|
||||||
|
Method = theNode.MethodDetails?.MethodName,
|
||||||
|
NodeGuid = theNode.Guid,
|
||||||
|
Type = invokeType,
|
||||||
|
State = FlowInvokeInfo.RunState.None,
|
||||||
|
};
|
||||||
|
flowInvokeInfos.Add(id, flowInvokeInfo);
|
||||||
|
return flowInvokeInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<FlowInvokeInfo> GetAllInvokeInfos() => [.. flowInvokeInfos.Values];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置节点的运行时参数数据
|
/// 设置节点的运行时参数数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -138,8 +163,6 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置运行时上一节点
|
/// 设置运行时上一节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -178,7 +201,6 @@ namespace Serein.Library
|
|||||||
dictIgnoreNodeFlow.AddOrUpdate(node, (o) => false, (o, n) => false);
|
dictIgnoreNodeFlow.AddOrUpdate(node, (o) => false, (o, n) => false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取当前节点的运行时上一节点
|
/// 获取当前节点的运行时上一节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -252,67 +274,38 @@ namespace Serein.Library
|
|||||||
throw new InvalidOperationException($"透传{nodeModel}节点数据时发生异常:上一节点不存在数据");
|
throw new InvalidOperationException($"透传{nodeModel}节点数据时发生异常:上一节点不存在数据");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 开始
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重置
|
/// 重置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Reset()
|
public void Reset()
|
||||||
{
|
{
|
||||||
//foreach (var nodeObj in dictNodeFlowData.Values)
|
|
||||||
//{
|
|
||||||
// if (nodeObj is null)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable)
|
|
||||||
// {
|
|
||||||
// disposable?.Dispose();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//if (Tag != null && typeof(IDisposable).IsAssignableFrom(Tag?.GetType()) && Tag is IDisposable tagDisposable)
|
|
||||||
//{
|
|
||||||
// tagDisposable?.Dispose();
|
|
||||||
//}
|
|
||||||
this.dictNodeFlowData?.Clear();
|
this.dictNodeFlowData?.Clear();
|
||||||
ExceptionOfRuning = null;
|
ExceptionOfRuning = null;
|
||||||
NextOrientation = ConnectionInvokeType.None;
|
NextOrientation = ConnectionInvokeType.None;
|
||||||
RunState = RunState.Running;
|
RunState = RunState.Running;
|
||||||
|
flowInvokeInfos.Clear();
|
||||||
_guid = global::System.Guid.NewGuid().ToString();
|
_guid = global::System.Guid.NewGuid().ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束当前流程上下文
|
/// 结束当前流程上下文
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Exit()
|
public void Exit()
|
||||||
{
|
{
|
||||||
//foreach (var nodeObj in dictNodeFlowData.Values)
|
|
||||||
//{
|
|
||||||
// if (nodeObj is null)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (typeof(IDisposable).IsAssignableFrom(nodeObj?.GetType()) && nodeObj is IDisposable disposable)
|
|
||||||
// {
|
|
||||||
// disposable?.Dispose();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//if (Tag != null && typeof(IDisposable).IsAssignableFrom(Tag?.GetType()) && Tag is IDisposable tagDisposable)
|
|
||||||
//{
|
|
||||||
// tagDisposable?.Dispose();
|
|
||||||
//}
|
|
||||||
this.dictNodeFlowData?.Clear();
|
this.dictNodeFlowData?.Clear();
|
||||||
ExceptionOfRuning = null;
|
ExceptionOfRuning = null;
|
||||||
NextOrientation = ConnectionInvokeType.None;
|
NextOrientation = ConnectionInvokeType.None;
|
||||||
RunState = RunState.Completion;
|
RunState = RunState.Completion;
|
||||||
_guid = global::System.Guid.NewGuid().ToString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 释放当前上下文中的所有资源
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="keyValuePairs"></param>
|
||||||
private void Dispose(ref IDictionary<string, object> keyValuePairs)
|
private void Dispose(ref IDictionary<string, object> keyValuePairs)
|
||||||
{
|
{
|
||||||
foreach (var nodeObj in keyValuePairs.Values)
|
foreach (var nodeObj in keyValuePairs.Values)
|
||||||
@@ -370,7 +363,6 @@ namespace Serein.Library
|
|||||||
|
|
||||||
list.Clear();
|
list.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Dispose(ref IList<object> list)
|
private void Dispose(ref IList<object> list)
|
||||||
{
|
{
|
||||||
foreach (var nodeObj in list)
|
foreach (var nodeObj in list)
|
||||||
|
|||||||
@@ -398,7 +398,6 @@ namespace Serein.Library
|
|||||||
CancellationTokenSource cts = new CancellationTokenSource();
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
FlowResult flowResult;
|
FlowResult flowResult;
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
||||||
{
|
{
|
||||||
var node = flowCallTree.Get(startNodeGuid);
|
var node = flowCallTree.Get(startNodeGuid);
|
||||||
|
|||||||
@@ -249,11 +249,31 @@ namespace Serein.Library
|
|||||||
sourceNode = sourceNodeTemp;
|
sourceNode = sourceNodeTemp;
|
||||||
|
|
||||||
if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
|
if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
|
||||||
|
{
|
||||||
|
|
||||||
inputParameter = context.GetFlowData(sourceNode.Guid)?.Value;
|
inputParameter = context.GetFlowData(sourceNode.Guid)?.Value;
|
||||||
|
}
|
||||||
else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) // 立刻执行目标节点获取参数
|
else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) // 立刻执行目标节点获取参数
|
||||||
inputParameter = (await sourceNode.ExecutingAsync(context, CancellationToken.None)).Value;
|
{
|
||||||
|
if (context.IsRecordInvokeInfo)
|
||||||
|
{
|
||||||
|
var invokeInfo = context.NewInvokeInfo(NodeModel, sourceNode, FlowInvokeInfo.InvokeType.ArgSource);
|
||||||
|
var result = await sourceNode.ExecutingAsync(context, CancellationToken.None);
|
||||||
|
inputParameter = result.Value;
|
||||||
|
invokeInfo.UploadResultValue(result.Value);
|
||||||
|
invokeInfo.UploadState(FlowInvokeInfo.RunState.Succeed);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var result = await sourceNode.ExecutingAsync(context, CancellationToken.None);
|
||||||
|
inputParameter = result.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
|
||||||
throw new Exception("无效的 ArgDataSourceType");
|
throw new Exception("无效的 ArgDataSourceType");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. 表达式处理
|
// 5. 表达式处理
|
||||||
|
|||||||
@@ -37,11 +37,11 @@ namespace Serein.Library.Utils
|
|||||||
|
|
||||||
double avg = total / count;
|
double avg = total / count;
|
||||||
|
|
||||||
Console.WriteLine($"运行 {count} 次:");
|
SereinEnv.WriteLine(InfoType.INFO, $"运行 {count} 次:");
|
||||||
Console.WriteLine($"总耗时 :{total} 毫秒:");
|
SereinEnv.WriteLine(InfoType.INFO, $"总耗时 :{total} 毫秒:");
|
||||||
Console.WriteLine($"最大耗时:{max} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"最大耗时:{max} 毫秒");
|
||||||
Console.WriteLine($"最小耗时:{min} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"最小耗时:{min} 毫秒");
|
||||||
Console.WriteLine($"平均耗时:{avg} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{avg} 毫秒");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -68,11 +68,11 @@ namespace Serein.Library.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
double avg = total / count;
|
double avg = total / count;
|
||||||
Console.WriteLine($"运行 {count} 次:");
|
SereinEnv.WriteLine(InfoType.INFO, $"运行 {count} 次:");
|
||||||
Console.WriteLine($"总耗时 :{total} 毫秒:");
|
SereinEnv.WriteLine(InfoType.INFO, $"总耗时 :{total} 毫秒:");
|
||||||
Console.WriteLine($"最大耗时:{max} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"最大耗时:{max} 毫秒");
|
||||||
Console.WriteLine($"最小耗时:{min} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"最小耗时:{min} 毫秒");
|
||||||
Console.WriteLine($"平均耗时:{avg} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{avg} 毫秒");
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -100,11 +100,11 @@ namespace Serein.Library.Utils
|
|||||||
}
|
}
|
||||||
|
|
||||||
double avg = total / count;
|
double avg = total / count;
|
||||||
Console.WriteLine($"运行 {count} 次:");
|
SereinEnv.WriteLine(InfoType.INFO, $"运行 {count} 次:");
|
||||||
Console.WriteLine($"总耗时 :{total} 毫秒:");
|
SereinEnv.WriteLine(InfoType.INFO, $"总耗时 :{total} 毫秒:");
|
||||||
Console.WriteLine($"最大耗时:{max} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"最大耗时:{max} 毫秒");
|
||||||
Console.WriteLine($"最小耗时:{min} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"最小耗时:{min} 毫秒");
|
||||||
Console.WriteLine($"平均耗时:{avg} 毫秒");
|
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{avg} 毫秒");
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -127,8 +127,8 @@ namespace Serein.Library.Utils
|
|||||||
if (ms < min) min = ms;
|
if (ms < min) min = ms;
|
||||||
total += ms;
|
total += ms;
|
||||||
|
|
||||||
Console.WriteLine($"运行1次耗时 :{total} 毫秒:");
|
var tips = $"运行耗时 :{total} 毫秒:";
|
||||||
Debug.WriteLine($"运行1次耗时 :{total} 毫秒:");
|
SereinEnv.WriteLine(InfoType.INFO, tips);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,12 +185,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
|
||||||
FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
||||||
{
|
await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel));
|
||||||
var flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
|
|
||||||
return flowResult;
|
|
||||||
});
|
|
||||||
#else
|
#else
|
||||||
FlowResult flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
|
//FlowResult flowResult = await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
|
||||||
|
FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () => await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel));
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -412,9 +412,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var nodes = canvasModel.Nodes.ToList();
|
canvasModel.Nodes = [.. canvasModel.Nodes, nodeModel];
|
||||||
nodes.Add(nodeModel);
|
|
||||||
canvasModel.Nodes = nodes;
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -146,11 +146,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public RunState FlowState { get => currentFlowEnvironment.FlowState; set => currentFlowEnvironment.FlowState = value; }
|
public RunState FlowState { get => currentFlowEnvironment.FlowState; set => currentFlowEnvironment.FlowState = value; }
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/* /// <inheritdoc/>
|
||||||
public void ActivateFlipflopNode(string nodeGuid)
|
public void ActivateFlipflopNode(string nodeGuid)
|
||||||
{
|
{
|
||||||
currentFlowEnvironment.FlowControl.ActivateFlipflopNode(nodeGuid);
|
currentFlowEnvironment.FlowControl.ActivateFlipflopNode(nodeGuid);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/* /// <inheritdoc/>
|
/* /// <inheritdoc/>
|
||||||
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
|
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
|
||||||
@@ -167,10 +167,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
}*/
|
}*/
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<bool> ExitFlowAsync()
|
/* public async Task<bool> ExitFlowAsync()
|
||||||
{
|
{
|
||||||
return await currentFlowEnvironment.FlowControl.ExitFlowAsync();
|
return await currentFlowEnvironment.FlowControl.ExitFlowAsync();
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/* /// <inheritdoc/>
|
/* /// <inheritdoc/>
|
||||||
public void ExitRemoteEnv()
|
public void ExitRemoteEnv()
|
||||||
@@ -221,11 +221,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
SetProjectLoadingFlag(true);
|
SetProjectLoadingFlag(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/* /// <inheritdoc/>
|
||||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||||
{
|
{
|
||||||
currentFlowEnvironment.FlowControl.MonitorObjectNotification(nodeGuid, monitorData, sourceType);
|
currentFlowEnvironment.FlowControl.MonitorObjectNotification(nodeGuid, monitorData, sourceType);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool TryUnloadLibrary(string assemblyName)
|
public bool TryUnloadLibrary(string assemblyName)
|
||||||
@@ -274,7 +274,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
/*
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||||
@@ -286,7 +286,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
|
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
|
||||||
{
|
{
|
||||||
return await currentFlowEnvironment.FlowControl.StartFlowAsync<TResult>(startNodeGuid);
|
return await currentFlowEnvironment.FlowControl.StartFlowAsync<TResult>(startNodeGuid);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/* /// <inheritdoc/>
|
/* /// <inheritdoc/>
|
||||||
public async Task StartRemoteServerAsync(int port = 7525)
|
public async Task StartRemoteServerAsync(int port = 7525)
|
||||||
@@ -299,7 +299,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
currentFlowEnvironment.StopRemoteServer();
|
currentFlowEnvironment.StopRemoteServer();
|
||||||
}*/
|
}*/
|
||||||
|
/*
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void TerminateFlipflopNode(string nodeGuid)
|
public void TerminateFlipflopNode(string nodeGuid)
|
||||||
{
|
{
|
||||||
@@ -310,7 +310,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
||||||
{
|
{
|
||||||
currentFlowEnvironment.FlowControl.TriggerInterrupt(nodeGuid, expression, type);
|
currentFlowEnvironment.FlowControl.TriggerInterrupt(nodeGuid, expression, type);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void SetUIContextOperation(UIContextOperation uiContextOperation)
|
public void SetUIContextOperation(UIContextOperation uiContextOperation)
|
||||||
@@ -318,12 +318,12 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
currentFlowEnvironment.SetUIContextOperation(uiContextOperation);
|
currentFlowEnvironment.SetUIContextOperation(uiContextOperation);
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public void UseExternalIOC(ISereinIOC ioc)
|
public void UseExternalIOC(ISereinIOC ioc)
|
||||||
{
|
{
|
||||||
currentFlowEnvironment.FlowControl.UseExternalIOC(ioc);
|
currentFlowEnvironment.FlowControl.UseExternalIOC(ioc);
|
||||||
}
|
}*/
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
|
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ namespace Serein.NodeFlow.Model
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行节点对应的方法
|
/// 执行节点对应的方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -50,7 +49,6 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <returns>节点传回数据对象</returns>
|
/// <returns>节点传回数据对象</returns>
|
||||||
public virtual async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
|
public virtual async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 执行触发检查是否需要中断
|
// 执行触发检查是否需要中断
|
||||||
if (DebugSetting.IsInterrupt)
|
if (DebugSetting.IsInterrupt)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SingleGlobalDataNode : NodeModelBase, INodeContainer
|
public partial class SingleGlobalDataNode : NodeModelBase, INodeContainer
|
||||||
{
|
{
|
||||||
|
string INodeContainer.Guid => this.Guid;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局数据节点是基础节点
|
/// 全局数据节点是基础节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -58,12 +60,13 @@ namespace Serein.NodeFlow.Model
|
|||||||
DataNode = nodeModel;
|
DataNode = nodeModel;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else if (DataNode.Guid != nodeModel.Guid)
|
||||||
{
|
{
|
||||||
// 全局数据节点只有一个子控件
|
Env.FlowEdit.TakeOutNodeToContainer(DataNode.CanvasDetails.Guid, DataNode.Guid);
|
||||||
|
Env.FlowEdit.PlaceNodeToContainer(this.CanvasDetails.Guid, nodeModel.Guid, this.Guid);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -78,21 +78,29 @@ namespace Serein.NodeFlow.Model.Operation
|
|||||||
public override async Task<bool> ExecuteAsync()
|
public override async Task<bool> ExecuteAsync()
|
||||||
{
|
{
|
||||||
if (!ValidationParameter()) return false;
|
if (!ValidationParameter()) return false;
|
||||||
|
var isSuccess = ContainerNode.PlaceNode(Node);
|
||||||
ContainerNode.PlaceNode(Node);
|
if(isSuccess is true)
|
||||||
|
|
||||||
await TriggerEvent(() =>
|
|
||||||
{
|
{
|
||||||
flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, ContainerNodeGuid)); // 通知UI更改节点放置位置
|
await TriggerEvent(() =>
|
||||||
});
|
{
|
||||||
return true;
|
flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, ContainerNodeGuid)); // 通知UI更改节点放置位置
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return isSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Undo()
|
public override bool Undo()
|
||||||
{
|
{
|
||||||
ContainerNode.TakeOutNode(Node);
|
var isSuccess = ContainerNode.TakeOutNode(Node);
|
||||||
flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, NodeGuid)); // 重新放置在画布上
|
if (isSuccess is true)
|
||||||
return true;
|
{
|
||||||
|
_ = TriggerEvent(() =>
|
||||||
|
{
|
||||||
|
// 取出节点,重新放置在画布上
|
||||||
|
flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, ContainerNode.Guid, NodeGuid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return isSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -65,23 +65,29 @@ namespace Serein.NodeFlow.Model.Operation
|
|||||||
{
|
{
|
||||||
if (!ValidationParameter()) return false;
|
if (!ValidationParameter()) return false;
|
||||||
|
|
||||||
ContainerNode.TakeOutNode(Node);
|
var isSuccess = ContainerNode.TakeOutNode(Node);
|
||||||
await TriggerEvent(() =>
|
if (isSuccess is true)
|
||||||
{
|
{
|
||||||
flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, NodeGuid)); // 重新放置在画布上
|
await TriggerEvent(() =>
|
||||||
});
|
{
|
||||||
return true;
|
// 取出节点,重新放置在画布上
|
||||||
|
flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, ContainerNode.Guid, NodeGuid));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return isSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Undo()
|
public override bool Undo()
|
||||||
{
|
{
|
||||||
ContainerNode.PlaceNode(Node);
|
var isSuccess = ContainerNode.PlaceNode(Node);
|
||||||
if (ContainerNode is IFlowNode containerFlowNode)
|
if (isSuccess is true)
|
||||||
{
|
{
|
||||||
flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, containerFlowNode.Guid)); // 通知UI更改节点放置位置
|
if (ContainerNode is IFlowNode containerFlowNode)
|
||||||
|
{
|
||||||
|
flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, containerFlowNode.Guid)); // 通知UI更改节点放置位置
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return isSuccess;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ using Serein.NodeFlow.Model;
|
|||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Linq;
|
||||||
using System.Threading.Tasks.Dataflow;
|
using System.Threading.Tasks.Dataflow;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
|
|
||||||
@@ -21,7 +24,6 @@ namespace Serein.NodeFlow.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
|
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束运行时需要执行的方法
|
/// 结束运行时需要执行的方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -271,13 +273,65 @@ namespace Serein.NodeFlow.Services
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<FlowResult> StartFlowInSelectNodeAsync(IFlowNode startNode)
|
public async Task<FlowResult> StartFlowInSelectNodeAsync(IFlowNode startNode)
|
||||||
{
|
{
|
||||||
var pool = WorkOptions.FlowContextPool;
|
var sw = Stopwatch.StartNew();
|
||||||
var context = pool.Allocate();
|
var checkpoints = new Dictionary<string, TimeSpan>();
|
||||||
var token = WorkOptions.CancellationTokenSource.Token;
|
|
||||||
var result = await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
|
|
||||||
|
|
||||||
|
var pool = WorkOptions.FlowContextPool;
|
||||||
|
var token = WorkOptions.CancellationTokenSource.Token;
|
||||||
|
var context = pool.Allocate();
|
||||||
|
checkpoints["准备Context"] = sw.Elapsed;
|
||||||
|
|
||||||
|
var result = await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
|
||||||
|
checkpoints["执行流程"] = sw.Elapsed;
|
||||||
|
|
||||||
|
|
||||||
|
if (context.IsRecordInvokeInfo)
|
||||||
|
{
|
||||||
|
var invokeInfos = context.GetAllInvokeInfos();
|
||||||
|
_ = Task.Delay(100).ContinueWith(async (task) =>
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
if(invokeInfos.Count < 255)
|
||||||
|
{
|
||||||
|
foreach (var info in invokeInfos)
|
||||||
|
{
|
||||||
|
SereinEnv.WriteLine(InfoType.INFO, info.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
double total = 0;
|
||||||
|
for (int i = 0; i < invokeInfos.Count; i++)
|
||||||
|
{
|
||||||
|
total += invokeInfos[i].TS.TotalSeconds;
|
||||||
|
}
|
||||||
|
SereinEnv.WriteLine(InfoType.INFO, $"运行次数:{invokeInfos.Count}");
|
||||||
|
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{total / invokeInfos.Count}");
|
||||||
|
SereinEnv.WriteLine(InfoType.INFO, $"总耗时:{total}");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
context.Reset();
|
context.Reset();
|
||||||
|
checkpoints["重置流程"] = sw.Elapsed;
|
||||||
|
|
||||||
pool.Free(context);
|
pool.Free(context);
|
||||||
|
checkpoints["释放Context"] = sw.Elapsed;
|
||||||
|
|
||||||
|
_ = Task.Run(() =>
|
||||||
|
{
|
||||||
|
var last = TimeSpan.Zero;
|
||||||
|
foreach (var kv in checkpoints)
|
||||||
|
{
|
||||||
|
SereinEnv.WriteLine(InfoType.INFO, $"{kv.Key} 耗时: {(kv.Value - last).TotalMilliseconds} ms");
|
||||||
|
last = kv.Value;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,7 +349,6 @@ namespace Serein.NodeFlow.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试移除全局触发器
|
/// 尝试移除全局触发器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
var result = nodeContainerControl.PlaceNode(this);
|
var result = nodeContainerControl.PlaceNode(this);
|
||||||
if (!result) // 检查是否放置成功,如果不成功,需要重新添加回来
|
if (!result) // 检查是否放置成功,如果不成功,需要重新添加回来
|
||||||
{
|
{
|
||||||
FlowCanvas.Add(this); // 从画布上移除
|
FlowCanvas.Add(this);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,7 +57,7 @@
|
|||||||
<TextBox IsEnabled="{Binding IsEnabledOnView}" MinWidth="50" Margin="2" Text="{Binding NodeModel.KeyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
<TextBox IsEnabled="{Binding IsEnabledOnView}" MinWidth="50" Margin="2" Text="{Binding NodeModel.KeyName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||||
HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||||
</TextBox>
|
</TextBox>
|
||||||
<Button Content="EXP" Command="{Binding CommandCopyDataExp}" Height="17.2"></Button>
|
<!--<Button Content="EXP" Command="{Binding CommandCopyDataExp}" Height="17.2"></Button>-->
|
||||||
<!--<Button Content="刷新 " Command="{Binding CommandCopyDataExp}" Height="17.2" Margin="2,0,0,0"></Button>-->
|
<!--<Button Content="刷新 " Command="{Binding CommandCopyDataExp}" Height="17.2" Margin="2,0,0,0"></Button>-->
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ namespace Serein.Workbench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class GlobalDataControl : NodeControlBase, INodeJunction, INodeContainerControl
|
public partial class GlobalDataControl : NodeControlBase, INodeJunction, INodeContainerControl
|
||||||
{
|
{
|
||||||
|
private readonly GlobalDataNodeControlViewModel viewModel;
|
||||||
|
|
||||||
public GlobalDataControl() : base()
|
public GlobalDataControl() : base()
|
||||||
{
|
{
|
||||||
// 窗体初始化需要
|
// 窗体初始化需要
|
||||||
@@ -26,6 +28,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
viewModel.NodeModel.DisplayName = "[全局数据]";
|
viewModel.NodeModel.DisplayName = "[全局数据]";
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
this.viewModel = viewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -56,6 +59,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
//viewModel.NodeModel is SingleGlobalDataNode
|
||||||
GlobalDataPanel.Children.Add(nodeControl);
|
GlobalDataPanel.Children.Add(nodeControl);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -261,11 +261,15 @@ namespace Serein.Workbench.Services
|
|||||||
private void FlowEEForwardingService_OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
|
private void FlowEEForwardingService_OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
string nodeGuid = eventArgs.NodeGuid;
|
string nodeGuid = eventArgs.NodeGuid;
|
||||||
if (!TryGetControl(nodeGuid, out var nodeControl))
|
string containerNodeGuid = eventArgs.ContainerNodeGuid;
|
||||||
|
if (!TryGetControl(containerNodeGuid, out var containerNodeControl) || !TryGetControl(nodeGuid, out var nodeControl))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
nodeControl.TakeOutContainer(); // 从容器节点中取出
|
nodeControl.TakeOutContainer(); // 从容器节点中取出
|
||||||
|
(double x, double y) = (Canvas.GetLeft(containerNodeControl), Canvas.GetRight(containerNodeControl));
|
||||||
|
Canvas.SetLeft(nodeControl, x + 400);
|
||||||
|
Canvas.SetRight(nodeControl, y + 200);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FlowEEForwardingService_OnNodePlace(NodePlaceEventArgs eventArgs)
|
private void FlowEEForwardingService_OnNodePlace(NodePlaceEventArgs eventArgs)
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
<!--<nodeView:NetScriptNodeControl x:Name="NetScriptNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
<!--<nodeView:NetScriptNodeControl x:Name="NetScriptNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
||||||
<nodeView:FlowCallNodeControl MaxWidth="110" MaxHeight="160" x:Name="FlowCallNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
<nodeView:FlowCallNodeControl MaxWidth="110" MaxHeight="160" x:Name="FlowCallNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||||
<nodeView:ScriptNodeControl MaxWidth="110" MaxHeight="160" x:Name="ScriptNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
<nodeView:ScriptNodeControl MaxWidth="110" MaxHeight="160" x:Name="ScriptNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||||
<!--<nodeView:GlobalDataControl MaxWidth="110" MaxHeight="160" x:Name="GlobalDataControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
<nodeView:GlobalDataControl MaxWidth="110" MaxHeight="160" x:Name="GlobalDataControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||||
<!--<nodeView:ExpOpNodeControl MaxWidth="110" MaxHeight="160" x:Name="ExpOpNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
<!--<nodeView:ExpOpNodeControl MaxWidth="110" MaxHeight="160" x:Name="ExpOpNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
||||||
<!--<nodeView:ConditionNodeControl MaxWidth="110" MaxHeight="160" x:Name="ConditionNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
<!--<nodeView:ConditionNodeControl MaxWidth="110" MaxHeight="160" x:Name="ConditionNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
||||||
<!--<nodeView:ConditionRegionControl x:Name="ConditionRegionControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
<!--<nodeView:ConditionRegionControl x:Name="ConditionRegionControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>-->
|
||||||
|
|||||||
@@ -122,7 +122,7 @@ namespace Serein.Workbench.Views
|
|||||||
private readonly TranslateTransform translateTransform;
|
private readonly TranslateTransform translateTransform;
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 初始化
|
#region 初始化画布与相关事件
|
||||||
|
|
||||||
public FlowCanvasView(FlowCanvasDetails model)
|
public FlowCanvasView(FlowCanvasDetails model)
|
||||||
{
|
{
|
||||||
@@ -173,11 +173,9 @@ namespace Serein.Workbench.Views
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private void InitEvent()
|
private void InitEvent()
|
||||||
{
|
{
|
||||||
keyEventService.OnKeyDown += KeyEventService_OnKeyDown;
|
keyEventService.OnKeyDown += KeyEventService_OnKeyDown;
|
||||||
//flowNodeService.OnRemoveConnectionLine += FlowNodeService_OnRemoveConnectionLine;
|
|
||||||
flowEEForwardingService.NodeLocated += FlowEEForwardingService_OnNodeLocated;
|
flowEEForwardingService.NodeLocated += FlowEEForwardingService_OnNodeLocated;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,8 +355,7 @@ namespace Serein.Workbench.Views
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region 画布节点操作接口实现
|
||||||
#region 接口实现
|
|
||||||
private IFlowCanvas Api => this;
|
private IFlowCanvas Api => this;
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -568,14 +565,20 @@ namespace Serein.Workbench.Views
|
|||||||
if (keyEventService.GetKeyState(Key.LeftCtrl) || keyEventService.GetKeyState(Key.RightCtrl))
|
if (keyEventService.GetKeyState(Key.LeftCtrl) || keyEventService.GetKeyState(Key.RightCtrl))
|
||||||
{
|
{
|
||||||
// Ctrl + F5 调试当前流程
|
// Ctrl + F5 调试当前流程
|
||||||
_ = flowEnvironment.FlowControl.StartFlowAsync([flowNodeService.CurrentSelectCanvas.Guid]);
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
flowEnvironment.FlowControl.StartFlowAsync([flowNodeService.CurrentSelectCanvas.Guid]);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
else if (selectNodeControls.Count == 1 )
|
else if (selectNodeControls.Count == 1 )
|
||||||
{
|
{
|
||||||
// F5 调试当前选定节点
|
// F5 调试当前选定节点
|
||||||
var nodeModel = selectNodeControls[0].ViewModel.NodeModel;
|
var nodeModel = selectNodeControls[0].ViewModel.NodeModel;
|
||||||
SereinEnv.WriteLine(InfoType.INFO, $"调试运行当前节点:{nodeModel.Guid}");
|
SereinEnv.WriteLine(InfoType.INFO, $"调试运行当前节点:{nodeModel.Guid}");
|
||||||
_ = flowEnvironment.FlowControl.StartFlowAsync<FlowResult>(nodeModel.Guid);
|
Task.Run(() =>
|
||||||
|
{
|
||||||
|
flowEnvironment.FlowControl.StartFlowAsync<FlowResult>(nodeModel.Guid);
|
||||||
|
});
|
||||||
//_ = nodeModel.StartFlowAsync(new DynamicContext(flowEnvironment), new CancellationToken());
|
//_ = nodeModel.StartFlowAsync(new DynamicContext(flowEnvironment), new CancellationToken());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user