mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
1. 重新设计了 JSON门户类的实现
2. Script脚本添加了原始字符串的实现 3. 修复了Script中无法对 \" 双引号转义的问题 4. 新增了对于集合嵌套取值的支持(目前仅是集合取值) 5. 重新设计了FlowWorkManagement任务启动的逻辑,修复了触发器无法正常运行的问题 6. 在ScriptBaseFunc中新增了 json() 本地函数,支持将字符串转为IJsonToken进行取值。 7. EmitHelper对于集合取值时,反射获取“get_item”委托时存在看你多个MethodInfo,现在可以传入子项类型,帮助匹配目标重载方法
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
using Serein.Library;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.NodeFlow.Services;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
@@ -52,9 +54,8 @@ namespace Serein.NodeFlow.Env
|
||||
private ObjectPool<FlowWorkManagement> flowTaskManagementPool;
|
||||
private FlowWorkOptions flowTaskOptions;
|
||||
|
||||
|
||||
|
||||
|
||||
private FlowWorkManagement? flowWorkManagement;
|
||||
private ISereinIOC? externalIOC;
|
||||
private Action<ISereinIOC>? setDefultMemberOnReset;
|
||||
private bool IsUseExternalIOC = false;
|
||||
@@ -88,9 +89,28 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<FlowWorkManagement> flowWorkManagements = [];
|
||||
private FlowWorkManagement GetFWM()
|
||||
{
|
||||
var fwm = flowTaskManagementPool.Allocate();
|
||||
flowWorkManagements.Add(fwm);
|
||||
return fwm;
|
||||
}
|
||||
private void ReturnFWM(FlowWorkManagement fwm)
|
||||
{
|
||||
if (flowWorkManagements.Contains(fwm))
|
||||
{
|
||||
flowWorkManagements.Remove(fwm);
|
||||
}
|
||||
fwm.Exit();
|
||||
flowTaskManagementPool.Free(fwm);
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||
{
|
||||
|
||||
#region 校验参数
|
||||
HashSet<string> guids = new HashSet<string>();
|
||||
bool isBreak = false;
|
||||
@@ -123,47 +143,49 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
// 初始化每个画布的数据,转换为流程任务
|
||||
var flowTasks = guids.Select(guid =>
|
||||
{
|
||||
if (!flowModelService.TryGetCanvasModel(guid, out var canvasModel))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,将不会运行。{guid}");
|
||||
return default;
|
||||
}
|
||||
if (canvasModel.StartNode is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将不会运行。{guid}");
|
||||
return default;
|
||||
}
|
||||
return canvasModel;
|
||||
})
|
||||
.Where(canvasModel => canvasModel != default && canvasModel.StartNode != null)
|
||||
.OfType<FlowCanvasDetails>()
|
||||
.ToDictionary(key => key.Guid,
|
||||
value => new FlowTask
|
||||
{
|
||||
GetStartNode = () => value.StartNode!,
|
||||
GetNodes = () => flowModelService.GetAllNodeModel(value.Guid),
|
||||
IsWaitStartFlow = false
|
||||
});
|
||||
|
||||
#region 初始化每个画布的数据,转换为流程任务
|
||||
Dictionary<string, FlowTask> flowTasks = [];
|
||||
foreach (var guid in guids)
|
||||
|
||||
if(flowTasks.Values.Count == 0)
|
||||
{
|
||||
if (!flowModelService.TryGetCanvasModel(guid, out var canvasModel))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}");
|
||||
return false;
|
||||
}
|
||||
var ft = new FlowTask();
|
||||
ft.GetNodes = () => flowModelService.GetAllNodeModel(guid);
|
||||
if (canvasModel.StartNode?.Guid is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}");
|
||||
return false;
|
||||
}
|
||||
ft.GetStartNode = () => canvasModel.StartNode;
|
||||
flowTasks.Add(guid, ft);
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
IOC.Reset();
|
||||
|
||||
// 初始化IOC
|
||||
setDefultMemberOnReset?.Invoke(IOC);
|
||||
IOC.Reset();
|
||||
IOC.Register<IFlowEnvironment>(() => flowEnvironment);
|
||||
//externalIOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
||||
|
||||
var flowTaskOptions = new FlowWorkOptions
|
||||
{
|
||||
FlowIOC = IOC,
|
||||
Environment = flowEnvironment, // 流程
|
||||
Flows = flowTasks,
|
||||
FlowContextPool = contexts, // 上下文对象池
|
||||
AutoRegisterTypes = flowLibraryService.GetaAutoRegisterType(), // 需要自动实例化的类型
|
||||
InitMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Init),
|
||||
LoadMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Loading),
|
||||
ExitMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Exit),
|
||||
};
|
||||
|
||||
|
||||
flowWorkManagement = new FlowWorkManagement(flowTaskOptions);
|
||||
var cts = new CancellationTokenSource();
|
||||
var flowWorkManagement = GetFWM();
|
||||
flowWorkManagement.WorkOptions.Flows = flowTasks;
|
||||
flowWorkManagement.WorkOptions.AutoRegisterTypes = flowLibraryService.GetaAutoRegisterType(); // 需要自动实例化的类型
|
||||
flowWorkManagement.WorkOptions.InitMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Init);
|
||||
flowWorkManagement.WorkOptions.LoadMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Loading);
|
||||
flowWorkManagement.WorkOptions.ExitMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Exit);
|
||||
using var cts = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
var t = await flowWorkManagement.RunAsync(cts.Token);
|
||||
@@ -174,22 +196,18 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}"); ;
|
||||
}
|
||||
flowTaskOptions = null;
|
||||
ReturnFWM(flowWorkManagement);
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
|
||||
{
|
||||
|
||||
var sw = Stopwatch.StartNew();
|
||||
var checkpoints = new Dictionary<string, TimeSpan>();
|
||||
|
||||
var flowTaskManagement = flowTaskManagementPool.Allocate();
|
||||
|
||||
var flowWorkManagement = GetFWM();
|
||||
if (!flowModelService.TryGetNodeModel(startNodeGuid, out IFlowNode? nodeModel))
|
||||
{
|
||||
throw new Exception($"节点不存在【{startNodeGuid}】");
|
||||
@@ -200,10 +218,10 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
|
||||
var flowContextPool = flowTaskManagement.WorkOptions.FlowContextPool;
|
||||
var flowContextPool = flowWorkManagement.WorkOptions.FlowContextPool;
|
||||
var context = flowContextPool.Allocate();
|
||||
checkpoints["准备调用环境"] = sw.Elapsed;
|
||||
var flowResult = await nodeModel.StartFlowAsync(context, flowTaskManagement.WorkOptions.CancellationTokenSource.Token); // 开始运行时从选定节点开始运行
|
||||
var flowResult = await nodeModel.StartFlowAsync(context, flowWorkManagement.WorkOptions.CancellationTokenSource.Token); // 开始运行时从选定节点开始运行
|
||||
checkpoints["调用节点流程"] = sw.Elapsed;
|
||||
|
||||
var last = TimeSpan.Zero;
|
||||
@@ -241,7 +259,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
context.Reset();
|
||||
flowContextPool.Free(context);
|
||||
flowTaskManagementPool.Free(flowTaskManagement);
|
||||
ReturnFWM(flowWorkManagement); // 释放流程任务管理器
|
||||
if (flowResult.Value is TResult result)
|
||||
{
|
||||
return result;
|
||||
@@ -256,32 +274,24 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
}
|
||||
|
||||
/*/// <summary>
|
||||
/// 单独运行一个节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
||||
{
|
||||
object result = Unit.Default;
|
||||
if (this.NodeModels.TryGetValue(nodeGuid, out var model))
|
||||
{
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
result = await model.ExecutingAsync(context, cts.Token);
|
||||
cts?.Cancel();
|
||||
}
|
||||
return result;
|
||||
}*/
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> ExitFlowAsync()
|
||||
{
|
||||
flowWorkManagement?.Exit();
|
||||
foreach(var flowWorkManagement in flowWorkManagements)
|
||||
{
|
||||
flowWorkManagement.Exit();
|
||||
}
|
||||
UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnFlowRunComplete(new FlowEventArgs()));
|
||||
IOC.Reset();
|
||||
flowWorkManagement = null;
|
||||
GC.Collect();
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
@@ -313,6 +323,7 @@ namespace Serein.NodeFlow.Env
|
||||
flowTaskManagement.TerminateGlobalFlipflopRuning(flipflopNode);
|
||||
}*/
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC>? setDefultMemberOnReset = null)
|
||||
{
|
||||
@@ -320,11 +331,13 @@ namespace Serein.NodeFlow.Env
|
||||
this.setDefultMemberOnReset = setDefultMemberOnReset;
|
||||
IsUseExternalIOC = true;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||
{
|
||||
flowEnvironmentEvent.OnMonitorObjectChanged(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType));
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
||||
{
|
||||
|
||||
@@ -242,7 +242,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// <param name="message">日志内容</param>
|
||||
/// <param name="type">日志类别</param>
|
||||
/// <param name="class">日志级别</param>
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial)
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.General)
|
||||
{
|
||||
currentFlowEnvironment.WriteLine(type, message, @class);
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// <summary>
|
||||
/// 信息输出等级
|
||||
/// </summary>
|
||||
public InfoClass InfoClass { get; set; } = InfoClass.Trivial;
|
||||
public InfoClass InfoClass { get; set; } = InfoClass.Debug;
|
||||
|
||||
/// <summary>
|
||||
/// 如果没有全局触发器,且没有循环分支,流程执行完成后自动为 Completion 。
|
||||
@@ -211,7 +211,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// <param name="message">日志内容</param>
|
||||
/// <param name="type">日志类别</param>
|
||||
/// <param name="class">日志级别</param>
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial)
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.General)
|
||||
{
|
||||
if (@class >= this.InfoClass)
|
||||
{
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 是否异步启动流程
|
||||
/// </summary>
|
||||
public bool IsTaskAsync { get; set; }
|
||||
public bool IsWaitStartFlow { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// 流程起始节点
|
||||
@@ -28,7 +28,7 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 节点任务执行依赖
|
||||
/// </summary>
|
||||
public class FlowWorkOptions()
|
||||
public sealed class FlowWorkOptions()
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程IOC容器
|
||||
|
||||
@@ -59,7 +59,7 @@ namespace Serein.NodeFlow.Model.Nodes
|
||||
if (token.IsCancellationRequested) { return null; }
|
||||
}
|
||||
|
||||
MethodDetails? md = MethodDetails;
|
||||
MethodDetails md = MethodDetails;
|
||||
if (md is null)
|
||||
{
|
||||
throw new Exception($"节点{Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
@@ -71,7 +71,7 @@ namespace Serein.NodeFlow.Model.Nodes
|
||||
|
||||
if (md.IsStatic)
|
||||
{
|
||||
object[] args = await this.GetParametersAsync(context, token);
|
||||
object[] args = md.ParameterDetailss.Length == 0 ? [] : await this.GetParametersAsync(context, token);
|
||||
var result = await dd.InvokeAsync(null, args);
|
||||
var flowReslt = FlowResult.OK(this.Guid, context, result);
|
||||
return flowReslt;
|
||||
|
||||
@@ -5,10 +5,22 @@ using System;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Nodes
|
||||
{
|
||||
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
|
||||
public partial class SingleFlipflopNode
|
||||
{
|
||||
/// <summary>
|
||||
/// <para>是否等待后继节点(仅对于全局触发器)</para>
|
||||
/// <para>如果为 true,则在触发器获取结果后,等待后继节点执行完成,才会调用触发器</para>
|
||||
/// <para>如果为 false,则触发器获取到结果后,将使用 _ = Task.Run(...) 再次调用触发器</para>
|
||||
/// </summary>
|
||||
|
||||
private bool _isWaitSuccessorNodes = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发器节点
|
||||
/// </summary>
|
||||
public class SingleFlipflopNode : NodeModelBase
|
||||
public partial class SingleFlipflopNode : NodeModelBase
|
||||
{
|
||||
/// <summary>
|
||||
/// 构造一个新的单触发器节点实例。
|
||||
@@ -29,46 +41,52 @@ namespace Serein.NodeFlow.Model.Nodes
|
||||
/// <exception cref="Exception"></exception>
|
||||
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return FlowResult.Fail(Guid, context, "流程操作已取消");
|
||||
}
|
||||
|
||||
#region 执行前中断
|
||||
if (DebugSetting.IsInterrupt) // 执行触发前
|
||||
{
|
||||
string guid = Guid.ToString();
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"[{MethodDetails.MethodName}]进入中断");
|
||||
await DebugSetting.GetInterruptTask.Invoke();
|
||||
await Console.Out.WriteLineAsync($"[{MethodDetails.MethodName}]中断已取消,开始执行后继分支");
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"[{MethodDetails.MethodName}]中断已取消,开始执行后继分支");
|
||||
}
|
||||
#endregion
|
||||
|
||||
MethodDetails md = MethodDetails;
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
context.Exit();
|
||||
context.ExceptionOfRuning = new FlipflopException($"无法获取到委托 {md.MethodName} 的详细信息。请检查流程配置。");
|
||||
return FlowResult.Fail(Guid, context, "不存在对应委托");
|
||||
}
|
||||
|
||||
var instance = Env.FlowControl.IOC.Get(md.ActingInstanceType);
|
||||
|
||||
var ioc = Env.FlowControl.IOC;
|
||||
var instance = ioc.Get(md.ActingInstanceType);
|
||||
if (instance is null)
|
||||
{
|
||||
Env.FlowControl.IOC.Register(md.ActingInstanceType).Build();
|
||||
instance = Env.FlowControl.IOC.Get(md.ActingInstanceType);
|
||||
ioc.Register(md.ActingInstanceType).Build();
|
||||
instance = ioc.Get(md.ActingInstanceType);
|
||||
}
|
||||
await dd.InvokeAsync(instance, [context]);
|
||||
var args = await this.GetParametersAsync(context, token);
|
||||
|
||||
var args = MethodDetails.ParameterDetailss.Length == 0 ? [] : await this.GetParametersAsync(context, token);
|
||||
|
||||
// 因为这里会返回不确定的泛型 IFlipflopContext<TRsult>
|
||||
// 而我们只需要获取到 State 和 Value(返回的数据)
|
||||
// 所以使用 dynamic 类型接收
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
dynamic dynamicFlipflopContext = await dd.InvokeAsync(instance, args);
|
||||
FlipflopStateType flipflopStateType = dynamicFlipflopContext.State;
|
||||
dynamic flipflopContext = await dd.InvokeAsync(instance, args);
|
||||
FlipflopStateType flipflopStateType = flipflopContext.State;
|
||||
context.NextOrientation = flipflopStateType.ToContentType();
|
||||
|
||||
|
||||
if (dynamicFlipflopContext.Type == TriggerDescription.Overtime)
|
||||
if (flipflopContext.Type == TriggerDescription.Overtime)
|
||||
{
|
||||
throw new FlipflopException(MethodDetails.MethodName + "触发器超时触发。Guid" + Guid);
|
||||
}
|
||||
object result = dynamicFlipflopContext.Value;
|
||||
object result = flipflopContext.Value;
|
||||
var flowReslt = FlowResult.OK(this.Guid, context, result);
|
||||
return flowReslt;
|
||||
}
|
||||
|
||||
@@ -23,12 +23,12 @@ namespace Serein.NodeFlow.Services
|
||||
/// <summary>
|
||||
/// 触发器对应的Cts
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
|
||||
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> _globalFlipflops = [];
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行时需要执行的方法
|
||||
/// </summary>
|
||||
private Func<Task>? ExitAction { get; set; }
|
||||
private Func<Task>? _exitAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化选项
|
||||
@@ -51,60 +51,72 @@ namespace Serein.NodeFlow.Services
|
||||
/// <returns></returns>
|
||||
public async Task<bool> RunAsync(CancellationToken token)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var checkpoints = new Dictionary<string, TimeSpan>();
|
||||
|
||||
#region 注册所有节点所属的类的类型,如果注册失败则退出
|
||||
List<IFlowNode> nodes = new List<IFlowNode>();
|
||||
foreach (var item in WorkOptions.Flows.Values)
|
||||
var flowTask = WorkOptions.Flows.Values.ToArray();
|
||||
foreach (var item in flowTask)
|
||||
{
|
||||
var temp = item.GetNodes();
|
||||
var temp = item?.GetNodes?.Invoke() ;
|
||||
if (temp is null)
|
||||
continue;
|
||||
nodes.AddRange(temp);
|
||||
}
|
||||
if (!RegisterAllType(nodes))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
checkpoints["注册所有节点类型"] = sw.Elapsed; // 记录注册所有节点类型的时间
|
||||
#endregion
|
||||
|
||||
#region 调用所有流程类的Init、Load事件
|
||||
|
||||
var initState = await TryInit();
|
||||
if (!initState)
|
||||
{
|
||||
if (!initState)
|
||||
return false;
|
||||
}
|
||||
;
|
||||
checkpoints["调用Init事件"] = sw.Elapsed; // 记录调用Init事件的时间
|
||||
var loadState = await TryLoadAsync();
|
||||
if (!loadState)
|
||||
{
|
||||
if (!loadState)
|
||||
return false;
|
||||
}
|
||||
;
|
||||
checkpoints["调用Load事件"] = sw.Elapsed; // 记录调用Load事件的时间
|
||||
#endregion
|
||||
var last = TimeSpan.Zero;
|
||||
foreach (var kv in checkpoints)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"{kv.Key} 耗时: {(kv.Value - last).TotalMilliseconds} ms");
|
||||
last = kv.Value;
|
||||
}
|
||||
|
||||
// 开始调用流程
|
||||
foreach (var kvp in WorkOptions.Flows)
|
||||
{
|
||||
var guid = kvp.Key;
|
||||
var flow = kvp.Value;
|
||||
var flowNodes = flow.GetNodes();
|
||||
|
||||
var flowNodes = flow.GetNodes?.Invoke();
|
||||
if (flowNodes is null)
|
||||
continue;
|
||||
IFlowNode? startNode = flow.GetStartNode?.Invoke();
|
||||
// 找到流程的起始节点,开始运行
|
||||
IFlowNode startNode = flow.GetStartNode();
|
||||
if (startNode is null)
|
||||
continue;
|
||||
// 是否后台运行当前画布流程
|
||||
if (flow.IsTaskAsync)
|
||||
if (flow.IsWaitStartFlow)
|
||||
{
|
||||
_ = Task.Run(async () => await CallStartNode(startNode), token); // 后台调用流程中的触发器
|
||||
|
||||
_ = Task.Run(async () => await CallNode(startNode), token); // 后台调用流程中的触发器
|
||||
}
|
||||
else
|
||||
{
|
||||
await CallStartNode(startNode);
|
||||
await CallNode(startNode);
|
||||
|
||||
}
|
||||
_ = Task.Run(async () => await CallFlipflopNode(flow), token); // 后台调用流程中的触发器
|
||||
await CallFlipflopNode(flow); // 后台调用流程中的触发器
|
||||
}
|
||||
|
||||
// 等待流程运行完成
|
||||
await CallExit();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -153,6 +165,11 @@ namespace Serein.NodeFlow.Services
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试初始化
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task<bool> TryInit()
|
||||
{
|
||||
var env = WorkOptions.Environment;
|
||||
@@ -175,6 +192,12 @@ namespace Serein.NodeFlow.Services
|
||||
var isSuccessful = true;
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试加载流程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task<bool> TryLoadAsync()
|
||||
{
|
||||
var env = WorkOptions.Environment;
|
||||
@@ -198,6 +221,12 @@ namespace Serein.NodeFlow.Services
|
||||
return isSuccessful;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束流程时调用的方法
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task<bool> CallExit()
|
||||
{
|
||||
var env = WorkOptions.Environment;
|
||||
@@ -205,8 +234,6 @@ namespace Serein.NodeFlow.Services
|
||||
var pool = WorkOptions.FlowContextPool;
|
||||
var ioc = WorkOptions.FlowIOC;
|
||||
|
||||
// var fit = ioc.Get<FlowInterruptTool>();
|
||||
// fit.CancelAllTrigger(); // 取消所有中断
|
||||
foreach (var md in mds) // 结束时
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
|
||||
@@ -228,39 +255,44 @@ namespace Serein.NodeFlow.Services
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 调用流程中的触发器节点
|
||||
/// </summary>
|
||||
/// <param name="flow"></param>
|
||||
/// <returns></returns>
|
||||
private async Task CallFlipflopNode(FlowTask flow)
|
||||
{
|
||||
var env = WorkOptions.Environment;
|
||||
var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node
|
||||
|
||||
var nodes = flow.GetNodes?.Invoke();
|
||||
if (nodes is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "流程中没有触发器节点可供执行");
|
||||
return;
|
||||
}
|
||||
var flipflopNodes = nodes.Where(item => item is SingleFlipflopNode node
|
||||
&& node.DebugSetting.IsEnable
|
||||
&& node.NotExitPreviousNode())
|
||||
.Select(item => (SingleFlipflopNode)item);
|
||||
//.ToList();// 获取需要再运行开始之前启动的触发器节点
|
||||
|
||||
if (flipflopNodes.Count() > 0)
|
||||
{
|
||||
var tasks = flipflopNodes.Select(async node =>
|
||||
{
|
||||
await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器
|
||||
});
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
.OfType<SingleFlipflopNode>()
|
||||
.Select(async node =>
|
||||
{
|
||||
await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器
|
||||
});
|
||||
var tasks = flipflopNodes.ToArray();
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从某一个节点开始执行
|
||||
/// 从某个节点开始执行
|
||||
/// </summary>
|
||||
/// <param name="startNode"></param>
|
||||
/// <returns></returns>
|
||||
private async Task CallStartNode(IFlowNode startNode)
|
||||
private async Task CallNode(IFlowNode startNode)
|
||||
{
|
||||
var pool = WorkOptions.FlowContextPool;
|
||||
var token = WorkOptions.CancellationTokenSource.Token;
|
||||
var context = pool.Allocate();
|
||||
context.Reset();
|
||||
await startNode.StartFlowAsync(context, token);
|
||||
context.Exit();
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
return;
|
||||
}
|
||||
@@ -287,8 +319,6 @@ namespace Serein.NodeFlow.Services
|
||||
checkpoints["执行流程"] = sw.Elapsed;
|
||||
|
||||
context.Reset();
|
||||
checkpoints["重置流程"] = sw.Elapsed;
|
||||
|
||||
pool.Free(context);
|
||||
checkpoints["释放Context"] = sw.Elapsed;
|
||||
|
||||
@@ -307,15 +337,15 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试添加全局触发器
|
||||
/// 运行全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode"></param>
|
||||
/// <param name="env"></param>
|
||||
public async Task RunGlobalFlipflopAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode)
|
||||
{
|
||||
if (dictGlobalFlipflop.TryAdd(singleFlipFlopNode, new CancellationTokenSource()))
|
||||
using var cts = new CancellationTokenSource();
|
||||
if (_globalFlipflops.TryAdd(singleFlipFlopNode, cts))
|
||||
{
|
||||
var cts = dictGlobalFlipflop[singleFlipFlopNode];
|
||||
await FlipflopExecuteAsync(singleFlipFlopNode, cts.Token);
|
||||
}
|
||||
}
|
||||
@@ -326,7 +356,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// <param name="singleFlipFlopNode"></param>
|
||||
public void TerminateGlobalFlipflopRuning(SingleFlipflopNode singleFlipFlopNode)
|
||||
{
|
||||
if (dictGlobalFlipflop.TryRemove(singleFlipFlopNode, out var cts))
|
||||
if (_globalFlipflops.TryRemove(singleFlipFlopNode, out var cts))
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
@@ -341,7 +371,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// </summary>
|
||||
private void TerminateAllGlobalFlipflop()
|
||||
{
|
||||
foreach ((var node, var cts) in dictGlobalFlipflop)
|
||||
foreach ((var node, var cts) in _globalFlipflops)
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
@@ -349,37 +379,50 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
cts.Dispose();
|
||||
}
|
||||
dictGlobalFlipflop.Clear();
|
||||
_globalFlipflops.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode">需要全局监听信号的触发器</param>
|
||||
/// <param name="singleToken">单个触发器持有的</param>
|
||||
/// <param name="flipflopNode">需要全局监听信号的触发器</param>
|
||||
/// <param name="token">单个触发器持有的</param>
|
||||
/// <returns></returns>
|
||||
private async Task FlipflopExecuteAsync(SingleFlipflopNode singleFlipFlopNode,
|
||||
CancellationToken singleToken)
|
||||
private async Task FlipflopExecuteAsync(SingleFlipflopNode flipflopNode,
|
||||
CancellationToken token)
|
||||
{
|
||||
|
||||
var pool = WorkOptions.FlowContextPool;
|
||||
while (!singleToken.IsCancellationRequested && !singleToken.IsCancellationRequested)
|
||||
while (true)
|
||||
{
|
||||
if(token.IsCancellationRequested)
|
||||
{
|
||||
break;
|
||||
}
|
||||
var context = pool.Allocate(); // 从上下文池取出新的实例
|
||||
try
|
||||
{
|
||||
var context = pool.Allocate(); // 启动全局触发器时新建上下文
|
||||
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task
|
||||
context.AddOrUpdateFlowData(singleFlipFlopNode.Guid, newFlowData);
|
||||
var result = await flipflopNode.ExecutingAsync(context, token); // 等待触发获取结果
|
||||
context.AddOrUpdateFlowData(flipflopNode.Guid, result);
|
||||
if (context.NextOrientation == ConnectionInvokeType.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_ = Task.Run(() => CallSubsequentNode(singleFlipFlopNode, singleToken, pool, context)); // 重新启动触发器
|
||||
await CallSuccessorNodesAsync(flipflopNode, token, pool, context);
|
||||
/*if (flipflopNode.IsWaitSuccessorNodes)
|
||||
{
|
||||
_ = Task.Run(async () => await CallSuccessorNodesAsync(flipflopNode, token, pool, context));
|
||||
}
|
||||
else
|
||||
{
|
||||
await CallSuccessorNodesAsync(flipflopNode, token, pool, context);
|
||||
}*/
|
||||
|
||||
|
||||
}
|
||||
catch (FlipflopException ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{flipflopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
|
||||
if (ex.Type == FlipflopException.CancelClass.CancelFlow)
|
||||
{
|
||||
break;
|
||||
@@ -387,9 +430,15 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.Guid}]异常。"+ ex.Message);
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{flipflopNode.Guid}]异常。"+ ex.Message);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -402,7 +451,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// <param name="pool"></param>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
private static async Task? CallSubsequentNode(SingleFlipflopNode singleFlipFlopNode, CancellationToken singleToken, ObjectPool<IFlowContext> pool, IFlowContext context)
|
||||
private static async Task CallSuccessorNodesAsync(SingleFlipflopNode singleFlipFlopNode, CancellationToken singleToken, ObjectPool<IFlowContext> pool, IFlowContext context)
|
||||
{
|
||||
var flowState = context.NextOrientation; // 记录一下流程状态
|
||||
var nextNodes = singleFlipFlopNode.SuccessorNodes[ConnectionInvokeType.Upstream]; // 优先调用上游分支
|
||||
@@ -441,8 +490,6 @@ namespace Serein.NodeFlow.Services
|
||||
await nextNodes[i].StartFlowAsync(context, singleToken); // 启动执行触发器后继分支的节点
|
||||
}
|
||||
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -450,7 +497,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// </summary>
|
||||
public void Exit()
|
||||
{
|
||||
ExitAction?.Invoke();
|
||||
_exitAction?.Invoke();
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user