mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
重构了运行逻辑。上下文使用对象池封装,节点方法调用时间传递CancellationTokenSource用来中止任务
This commit is contained in:
@@ -26,12 +26,6 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
RunState RunState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 用来在当前流程上下文间传递数据
|
||||
/// </summary>
|
||||
//Dictionary<string, object> ContextShareData { get; }
|
||||
|
||||
object Tag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 下一个要执行的节点类别
|
||||
@@ -77,6 +71,11 @@ namespace Serein.Library.Api
|
||||
/// <param name="flowData"></param>
|
||||
void AddOrUpdate(string nodeGuid, object flowData);
|
||||
|
||||
/// <summary>
|
||||
/// 重置流程状态(用于对象池回收)
|
||||
/// </summary>
|
||||
void Reset();
|
||||
|
||||
/// <summary>
|
||||
/// 用以提前结束当前上下文流程的运行
|
||||
/// </summary>
|
||||
|
||||
@@ -693,7 +693,7 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 全局触发器运行状态
|
||||
/// </summary>
|
||||
RunState FlipFlopState { get; set; }
|
||||
//RunState FlipFlopState { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 表示当前环境
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Serein.Library.Api
|
||||
/// <param name="key">登记使用的名称</param>
|
||||
/// <param name="instance">实例对象</param>
|
||||
/// <returns>是否注册成功</returns>
|
||||
bool RegisterInstance(string key, object instance);
|
||||
/// bool RegisterInstance(string key, object instance);
|
||||
|
||||
/// <summary>
|
||||
/// 获取类型的实例。如果需要获取的类型以“接口-实现类”的方式注册,请使用接口的类型。
|
||||
@@ -67,7 +67,7 @@ namespace Serein.Library.Api
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key">登记实例时使用的Key</param>
|
||||
/// <returns></returns>
|
||||
T Get<T>(string key);
|
||||
/// T Get<T>(string key);
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -295,10 +295,10 @@ namespace Serein.Library
|
||||
{
|
||||
return (T)sereinIOC.Get(typeof(T));
|
||||
}
|
||||
T ISereinIOC.Get<T>(string key)
|
||||
{
|
||||
return sereinIOC.Get<T>(key);
|
||||
}
|
||||
//T ISereinIOC.Get<T>(string key)
|
||||
//{
|
||||
// return sereinIOC.Get<T>(key);
|
||||
//}
|
||||
|
||||
|
||||
bool ISereinIOC.RegisterPersistennceInstance(string key, object instance)
|
||||
@@ -311,10 +311,10 @@ namespace Serein.Library
|
||||
return sereinIOC.RegisterPersistennceInstance(key, instance);
|
||||
}
|
||||
|
||||
bool ISereinIOC.RegisterInstance(string key, object instance)
|
||||
{
|
||||
return sereinIOC.RegisterInstance(key, instance);
|
||||
}
|
||||
//bool ISereinIOC.RegisterInstance(string key, object instance)
|
||||
//{
|
||||
// return sereinIOC.RegisterInstance(key, instance);
|
||||
//}
|
||||
|
||||
|
||||
object ISereinIOC.Instantiate(Type type)
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Serein.Library
|
||||
RunState = RunState.Running;
|
||||
}
|
||||
|
||||
private readonly string _guid = global::System.Guid.NewGuid().ToString();
|
||||
private string _guid = global::System.Guid.NewGuid().ToString();
|
||||
string IDynamicContext.Guid => _guid;
|
||||
|
||||
/// <summary>
|
||||
@@ -35,11 +35,6 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
public RunState RunState { get; set; } = RunState.NoStart;
|
||||
|
||||
/// <summary>
|
||||
/// 用来在当前流程上下文间传递数据
|
||||
/// </summary>
|
||||
//public Dictionary<string, object> ContextShareData { get; } = new Dictionary<string, object>();
|
||||
public object Tag { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前节点执行完成后,设置该属性,让运行环境判断接下来要执行哪个分支的节点。
|
||||
@@ -133,6 +128,37 @@ namespace Serein.Library
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重置
|
||||
/// </summary>
|
||||
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();
|
||||
ExceptionOfRuning = null;
|
||||
NextOrientation = ConnectionInvokeType.None;
|
||||
RunState = RunState.Running;
|
||||
_guid = global::System.Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 结束当前流程上下文
|
||||
/// </summary>
|
||||
@@ -156,9 +182,11 @@ namespace Serein.Library
|
||||
//{
|
||||
// tagDisposable?.Dispose();
|
||||
//}
|
||||
this.Tag = null;
|
||||
this.dictNodeFlowData?.Clear();
|
||||
ExceptionOfRuning = null;
|
||||
NextOrientation = ConnectionInvokeType.None;
|
||||
RunState = RunState.Completion;
|
||||
_guid = global::System.Guid.NewGuid().ToString();
|
||||
}
|
||||
|
||||
private void Dispose(ref IDictionary<string, object> keyValuePairs)
|
||||
|
||||
@@ -45,8 +45,8 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 作用实例(多个相同的节点将会共享同一个实例)
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private object _actingInstance;
|
||||
// [PropertyInfo]
|
||||
// private object _actingInstance;
|
||||
|
||||
/// <summary>
|
||||
/// 方法名称
|
||||
@@ -237,16 +237,16 @@ namespace Serein.Library
|
||||
// this => 是元数据
|
||||
var md = new MethodDetails( nodeModel) // 创建新节点时拷贝实例
|
||||
{
|
||||
AssemblyName = this.AssemblyName,
|
||||
ActingInstance = this.ActingInstance,
|
||||
ActingInstanceType = this.ActingInstanceType,
|
||||
MethodDynamicType = this.MethodDynamicType,
|
||||
MethodAnotherName = this.MethodAnotherName,
|
||||
ReturnType = this.ReturnType,
|
||||
MethodName = this.MethodName,
|
||||
MethodLockName = this.MethodLockName,
|
||||
IsProtectionParameter = this.IsProtectionParameter,
|
||||
ParamsArgIndex = this.ParamsArgIndex,
|
||||
AssemblyName = this.AssemblyName, // 拷贝
|
||||
//ActingInstance = this.ActingInstance,
|
||||
ActingInstanceType = this.ActingInstanceType, // 拷贝
|
||||
MethodDynamicType = this.MethodDynamicType, // 拷贝
|
||||
MethodAnotherName = this.MethodAnotherName, // 拷贝
|
||||
ReturnType = this.ReturnType, // 拷贝
|
||||
MethodName = this.MethodName, // 拷贝
|
||||
MethodLockName = this.MethodLockName, // 拷贝
|
||||
IsProtectionParameter = this.IsProtectionParameter, // 拷贝
|
||||
ParamsArgIndex = this.ParamsArgIndex, // 拷贝
|
||||
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述
|
||||
};
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ namespace Serein.Library
|
||||
}
|
||||
|
||||
this.MethodDetails.ParameterDetailss = null;
|
||||
this.MethodDetails.ActingInstance = null;
|
||||
//this.MethodDetails.ActingInstance = null;
|
||||
this.MethodDetails.NodeModel = null;
|
||||
this.MethodDetails.ReturnType = null;
|
||||
this.MethodDetails.AssemblyName = null;
|
||||
@@ -249,43 +249,46 @@ namespace Serein.Library
|
||||
/// <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;
|
||||
}
|
||||
//public static bool IsBradk(IDynamicContext context)
|
||||
//{
|
||||
// // 上下文不再执行
|
||||
// if (context.RunState == RunState.Completion)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// 不存在全局触发器时,流程运行状态被设置为完成,退出执行,用于打断无限循环分支。
|
||||
if (flowCts is null && context.Env.FlowState == RunState.Completion)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// // 不存在全局触发器时,流程运行状态被设置为完成,退出执行,用于打断无限循环分支。
|
||||
// if (flowCts is null && context.Env.FlowState == RunState.Completion)
|
||||
// {
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// 如果存在全局触发器,且触发器的执行任务已经被取消时,退出执行。
|
||||
if (flowCts != null)
|
||||
{
|
||||
if (flowCts.IsCancellationRequested)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// // 如果存在全局触发器,且触发器的执行任务已经被取消时,退出执行。
|
||||
// if (flowCts != null)
|
||||
// {
|
||||
// if (flowCts.IsCancellationRequested)
|
||||
// return true;
|
||||
// }
|
||||
// return false;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="token">流程运行</param>
|
||||
/// <returns></returns>
|
||||
public async Task StartFlowAsync(IDynamicContext context)
|
||||
public async Task StartFlowAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
||||
HashSet<NodeModelBase> processedNodes = new HashSet<NodeModelBase>(); // 用于记录已处理上游节点的节点
|
||||
stack.Push(this);
|
||||
var flowCts = context.Env.IOC.Get<CancellationTokenSource>(NodeStaticConfig.FlipFlopCtsName);
|
||||
bool hasFlipflow = flowCts != null;
|
||||
while (stack.Count > 0) // 循环中直到栈为空才会退出循环
|
||||
while (context.RunState != RunState.Completion // 没有完成
|
||||
&& token.IsCancellationRequested == false // 没有取消
|
||||
&& stack.Count > 0) // 循环中直到栈为空才会退出循环
|
||||
{
|
||||
|
||||
|
||||
#if DEBUG
|
||||
await Task.Delay(1);
|
||||
#endif
|
||||
@@ -299,15 +302,12 @@ namespace Serein.Library
|
||||
object newFlowData;
|
||||
try
|
||||
{
|
||||
newFlowData = await currentNode.ExecutingAsync(context, token);
|
||||
|
||||
if (IsBradk(context, flowCts)) break; // 退出执行
|
||||
newFlowData = await currentNode.ExecutingAsync(context);
|
||||
if (IsBradk(context, flowCts)) break; // 退出执行
|
||||
if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||
}
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -316,9 +316,7 @@ namespace Serein.Library
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
context.ExceptionOfRuning = ex;
|
||||
}
|
||||
|
||||
|
||||
await RefreshFlowDataAndExpInterrupt(context, currentNode, newFlowData); // 执行当前节点后刷新数据
|
||||
context.AddOrUpdate(currentNode.Guid, newFlowData); // 上下文中更新数据
|
||||
#endregion
|
||||
|
||||
#region 执行完成
|
||||
@@ -355,14 +353,10 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
/// <param name="context">流程上下文</param>
|
||||
/// <returns>节点传回数据对象</returns>
|
||||
public virtual async Task<object> ExecutingAsync(IDynamicContext context)
|
||||
public virtual async Task<object> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
|
||||
#region 调试中断
|
||||
if(context.NextOrientation == ConnectionInvokeType.IsError)
|
||||
{
|
||||
}
|
||||
|
||||
// 执行触发检查是否需要中断
|
||||
if (DebugSetting.IsInterrupt)
|
||||
{
|
||||
@@ -370,8 +364,8 @@ namespace Serein.Library
|
||||
await DebugSetting.GetInterruptTask.Invoke();
|
||||
//await fit.WaitTriggerAsync(Guid); // 创建一个等待的中断任务
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"[{this.MethodDetails?.MethodName}]中断已取消,开始执行后继分支");
|
||||
var flowCts = context.Env.IOC.Get<CancellationTokenSource>(NodeStaticConfig.FlipFlopCtsName);
|
||||
if (IsBradk(context, flowCts)) return null; // 流程已终止,取消后续的执行
|
||||
//var flowCts = context.Env.IOC.Get<CancellationTokenSource>(NodeStaticConfig.FlipFlopCtsName);
|
||||
if (token.IsCancellationRequested) { return null; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -385,17 +379,14 @@ namespace Serein.Library
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||
}
|
||||
if (md.ActingInstance is null)
|
||||
var instance = Env.IOC.Get(md.ActingInstanceType);
|
||||
if(instance == null)
|
||||
{
|
||||
md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType);
|
||||
if (md.ActingInstance is null)
|
||||
{
|
||||
md.ActingInstance = context.Env.IOC.Instantiate(md.ActingInstanceType);
|
||||
}
|
||||
Env.IOC.Register(md.ActingInstanceType).Build();
|
||||
instance = Env.IOC.Get(md.ActingInstanceType);
|
||||
}
|
||||
|
||||
object[] args = await GetParametersAsync(context);
|
||||
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||
object[] args = await GetParametersAsync(context, token);
|
||||
var result = await dd.InvokeAsync(instance, args);
|
||||
return result;
|
||||
|
||||
}
|
||||
@@ -403,7 +394,7 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 获取对应的参数数组
|
||||
/// </summary>
|
||||
public async Task<object[]> GetParametersAsync(IDynamicContext context)
|
||||
public async Task<object[]> GetParametersAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
if (MethodDetails.ParameterDetailss.Length == 0)
|
||||
{
|
||||
@@ -454,13 +445,13 @@ namespace Serein.Library
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新节点数据,并检查监视表达式是否生效
|
||||
/// 检查监视表达式是否生效
|
||||
/// </summary>
|
||||
/// <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)
|
||||
public static async Task CheckExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object newData = null)
|
||||
{
|
||||
string guid = nodeModel.Guid;
|
||||
context.AddOrUpdate(guid, newData); // 上下文中更新数据
|
||||
|
||||
@@ -9,16 +9,6 @@ namespace Serein.Library
|
||||
{
|
||||
public static class NodeStaticConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 全局触发器CTS
|
||||
/// </summary>
|
||||
public const string FlipFlopCtsName = "$FlowFlipFlopCts";
|
||||
/// <summary>
|
||||
/// 流程运行CTS
|
||||
/// </summary>
|
||||
public const string FlowRungCtsName = "$FlowRungCtsName";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 节点的命名空间
|
||||
/// </summary>
|
||||
|
||||
160
Library/Utils/ObjectPool.cs
Normal file
160
Library/Utils/ObjectPool.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 具有预定义池大小限制的对象池模式的通用实现。其主要目的是将有限数量的经常使用的对象保留在池中,以便进一步回收。
|
||||
///
|
||||
/// 注:
|
||||
/// 1)目标不是保留所有返回的对象。池不是用来存储的。如果池中没有空间,则会丢弃额外返回的对象。
|
||||
///
|
||||
/// 2)这意味着如果对象是从池中获得的,调用者将在相对较短的时间内返回它
|
||||
/// 时间。长时间保持检出对象是可以的,但是会降低池的有用性。你只需要重新开始。
|
||||
///
|
||||
/// 不将对象返回给池并不会损害池的工作,但这是一种不好的做法。
|
||||
/// 基本原理:如果没有重用对象的意图,就不要使用pool——只使用“new”
|
||||
/// </summary>
|
||||
public class ObjectPool<T> where T : class
|
||||
{
|
||||
[DebuggerDisplay("{Value,nq}")]
|
||||
private struct Element
|
||||
{
|
||||
internal T Value;
|
||||
}
|
||||
|
||||
// 不使用System。Func{T},因为. net 2.0没有该类型。
|
||||
public delegate T Factory();
|
||||
|
||||
// 池对象的存储。第一个项存储在专用字段中,因为我们希望能够满足来自它的大多数请求。
|
||||
private T _firstItem;
|
||||
|
||||
private readonly Element[] _items;
|
||||
|
||||
// 工厂在池的生命周期内被存储。只有当池需要扩展时,我们才调用它。
|
||||
// 与“new T()”相比,Func为实现者提供了更多的灵活性,并且比“new T()”更快。
|
||||
private readonly Factory _factory;
|
||||
|
||||
public ObjectPool(Factory factory)
|
||||
: this(factory, Environment.ProcessorCount * 2)
|
||||
{ }
|
||||
|
||||
public ObjectPool(Factory factory, int size)
|
||||
{
|
||||
Debug.Assert(size >= 1);
|
||||
_factory = factory;
|
||||
_items = new Element[size - 1];
|
||||
}
|
||||
|
||||
private T CreateInstance()
|
||||
{
|
||||
T inst = _factory();
|
||||
return inst;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成实例。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 搜索策略是一种简单的线性探测,选择它是为了缓存友好。
|
||||
/// 请注意,Free会尝试将回收的对象存储在靠近起点的地方,从而在统计上减少我们通常搜索的距离。
|
||||
/// </remarks>
|
||||
public T Allocate()
|
||||
{
|
||||
/*
|
||||
* PERF:检查第一个元素。如果失败,AllocateSlow将查看剩余的元素。
|
||||
* 注意,初始读是乐观地不同步的。
|
||||
* 这是有意为之。只有了待使用对象,我们才会返回。
|
||||
* 在最坏的情况下,我们可能会错过一些最近返回的对象。没什么大不了的。
|
||||
*/
|
||||
T inst = _firstItem;
|
||||
if (inst == null || inst != Interlocked.CompareExchange(ref _firstItem, null, inst))
|
||||
{
|
||||
inst = AllocateSlow();
|
||||
}
|
||||
|
||||
return inst;
|
||||
}
|
||||
|
||||
private T AllocateSlow()
|
||||
{
|
||||
Element[] items = _items;
|
||||
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
// 注意,初始读是乐观地不同步的。这是有意为之。只有有了候选人,我们才会联系。在最坏的情况下,我们可能会错过一些最近返回的对象。没什么大不了的。
|
||||
T inst = items[i].Value;
|
||||
if (inst != null)
|
||||
{
|
||||
if (inst == Interlocked.CompareExchange(ref items[i].Value, null, inst))
|
||||
{
|
||||
return inst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return CreateInstance();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
///返回对象到池。
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// 搜索策略是一种简单的线性探测,选择它是因为它具有缓存友好性。
|
||||
/// 请注意Free会尝试将回收的对象存储在靠近起点的地方,从而在统计上减少我们通常在Allocate中搜索的距离。
|
||||
/// </remarks>
|
||||
public void Free(T obj)
|
||||
{
|
||||
Validate(obj);
|
||||
|
||||
if (_firstItem == null)
|
||||
{
|
||||
// 这里故意不使用联锁。在最坏的情况下,两个对象可能存储在同一个槽中。这是不太可能发生的,只意味着其中一个对象将被收集。
|
||||
_firstItem = obj;
|
||||
}
|
||||
else
|
||||
{
|
||||
FreeSlow(obj);
|
||||
}
|
||||
}
|
||||
|
||||
private void FreeSlow(T obj)
|
||||
{
|
||||
Element[] items = _items;
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
if (items[i].Value == null)
|
||||
{
|
||||
// 这里故意不使用联锁。在最坏的情况下,两个对象可能存储在同一个槽中。这是不太可能发生的,只意味着其中一个对象将被收集。
|
||||
items[i].Value = obj;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Conditional("DEBUG")]
|
||||
private void Validate(object obj)
|
||||
{
|
||||
Debug.Assert(obj != null, "freeing null?");
|
||||
|
||||
Debug.Assert(_firstItem != obj, "freeing twice?");
|
||||
|
||||
var items = _items;
|
||||
for (int i = 0; i < items.Length; i++)
|
||||
{
|
||||
var value = items[i].Value;
|
||||
if (value == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.Assert(value != obj, "freeing twice?");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Reactive;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
@@ -32,7 +33,7 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
this.sereinIOC = new SereinIOC();
|
||||
this.IsGlobalInterrupt = false;
|
||||
this.flowStarter = null;
|
||||
this.flowTaskManagement = null;
|
||||
this.sereinIOC.OnIOCMembersChanged += e =>
|
||||
{
|
||||
if (OperatingSystem.IsWindows())
|
||||
@@ -357,9 +358,9 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 流程启动器(每次运行时都会重新new一个)
|
||||
/// 流程任务管理
|
||||
/// </summary>
|
||||
private FlowStarter? flowStarter;
|
||||
private FlowWorkManagement? flowTaskManagement;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -381,29 +382,44 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 异步运行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartFlowAsync()
|
||||
{
|
||||
flowStarter ??= new FlowStarter();
|
||||
var nodes = NodeModels.Values.ToList();
|
||||
|
||||
List<MethodDetails> initMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init);
|
||||
List<MethodDetails> loadMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading);
|
||||
List<MethodDetails> exitMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit);
|
||||
Dictionary<RegisterSequence, List<Type>> autoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType();
|
||||
|
||||
IOC.Reset();
|
||||
await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods);
|
||||
//_ = Task.Run(async () =>
|
||||
//{
|
||||
// //if (FlipFlopState == RunState.Completion)
|
||||
// //{
|
||||
// // await ExitFlowAsync(); // 未运行触发器时,才会调用结束方法
|
||||
// //}
|
||||
//});
|
||||
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
||||
|
||||
var flowTaskOptions = new FlowTaskLibrary
|
||||
{
|
||||
|
||||
Environment = this,
|
||||
FlowContextPool = new ObjectPool<IDynamicContext>(() => new DynamicContext(this)),
|
||||
Nodes = NodeModels.Values.ToList(),
|
||||
AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(),
|
||||
InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
|
||||
LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading),
|
||||
ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit),
|
||||
|
||||
};
|
||||
flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
|
||||
var cts = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
var t =await flowTaskManagement.RunAsync(cts.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}"); ;
|
||||
}
|
||||
flowTaskOptions = null;
|
||||
return true;
|
||||
|
||||
|
||||
@@ -417,7 +433,7 @@ namespace Serein.NodeFlow.Env
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
|
||||
if (flowStarter is null)
|
||||
if (flowTaskManagement is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, "没有启动流程,无法运行单个节点");
|
||||
return false;
|
||||
@@ -435,7 +451,7 @@ namespace Serein.NodeFlow.Env
|
||||
//SerinExpressionEvaluator.Evaluate(setExp, nodeModel,out _);
|
||||
//var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
|
||||
|
||||
await flowStarter.StartFlowInSelectNodeAsync(this, nodeModel);
|
||||
await flowTaskManagement.StartFlowInSelectNodeAsync(this, nodeModel);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
@@ -454,7 +470,9 @@ namespace Serein.NodeFlow.Env
|
||||
object result = new Unit();
|
||||
if (this.NodeModels.TryGetValue(nodeGuid, out var model))
|
||||
{
|
||||
result = await model.ExecutingAsync(context);
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
result = await model.ExecutingAsync(context, cts.Token);
|
||||
cts?.Cancel();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -464,10 +482,10 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
public Task<bool> ExitFlowAsync()
|
||||
{
|
||||
flowStarter?.Exit();
|
||||
flowTaskManagement?.Exit();
|
||||
UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs()));
|
||||
IOC.Reset();
|
||||
flowStarter = null;
|
||||
flowTaskManagement = null;
|
||||
GC.Collect();
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
@@ -480,12 +498,12 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
var nodeModel = GuidToModel(nodeGuid);
|
||||
if (nodeModel is null) return;
|
||||
if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
||||
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
||||
{
|
||||
if (FlowState != RunState.Completion
|
||||
&& flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点
|
||||
{
|
||||
_ = flowStarter.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中
|
||||
_ = flowTaskManagement.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中
|
||||
|
||||
}
|
||||
}
|
||||
@@ -499,9 +517,9 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
var nodeModel = GuidToModel(nodeGuid);
|
||||
if (nodeModel is null) return;
|
||||
if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
||||
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
||||
{
|
||||
flowStarter.TerminateGlobalFlipflopRuning(flipflopNode);
|
||||
flowTaskManagement.TerminateGlobalFlipflopRuning(flipflopNode);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -793,7 +811,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
#endregion
|
||||
|
||||
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||
var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||
if (nodeModel is null)
|
||||
{
|
||||
nodeInfo.Guid = string.Empty;
|
||||
@@ -926,7 +944,7 @@ namespace Serein.NodeFlow.Env
|
||||
NodeModelBase? nodeModel;
|
||||
if (methodDetailsInfo is null)
|
||||
{
|
||||
nodeModel = FlowFunc.CreateNode(this, nodeControlType); // 加载基础节点
|
||||
nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType); // 加载基础节点
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -934,7 +952,7 @@ namespace Serein.NodeFlow.Env
|
||||
methodDetailsInfo.MethodName,
|
||||
out var methodDetails))
|
||||
{
|
||||
nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 一般的加载节点方法
|
||||
nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType, methodDetails); // 一般的加载节点方法
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1032,7 +1050,7 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
if (remoteNode is SingleFlipflopNode flipflopNode)
|
||||
{
|
||||
flowStarter?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被移除的是全局触发器,尝试从启动器移除
|
||||
flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被移除的是全局触发器,尝试从启动器移除
|
||||
}
|
||||
|
||||
remoteNode.Remove(); // 调用节点的移除方法
|
||||
@@ -1689,7 +1707,7 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
if (toNode is SingleFlipflopNode flipflopNode)
|
||||
{
|
||||
flowStarter?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除
|
||||
flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除
|
||||
}
|
||||
|
||||
var isPass = false;
|
||||
@@ -1896,10 +1914,10 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
return (T)sereinIOC.Get(typeof(T));
|
||||
}
|
||||
T ISereinIOC.Get<T>(string key)
|
||||
{
|
||||
return sereinIOC.Get<T>(key);
|
||||
}
|
||||
//T ISereinIOC.Get<T>(string key)
|
||||
//{
|
||||
// return sereinIOC.Get<T>(key);
|
||||
//}
|
||||
|
||||
|
||||
bool ISereinIOC.RegisterPersistennceInstance(string key, object instance)
|
||||
@@ -1908,10 +1926,10 @@ namespace Serein.NodeFlow.Env
|
||||
return sereinIOC.RegisterPersistennceInstance(key, instance);
|
||||
}
|
||||
|
||||
bool ISereinIOC.RegisterInstance(string key, object instance)
|
||||
{
|
||||
return sereinIOC.RegisterInstance(key, instance);
|
||||
}
|
||||
//bool ISereinIOC.RegisterInstance(string key, object instance)
|
||||
//{
|
||||
// return sereinIOC.RegisterInstance(key, instance);
|
||||
//}
|
||||
|
||||
|
||||
object ISereinIOC.Instantiate(Type type)
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
public InfoClass InfoClass { get => currentFlowEnvironment.InfoClass; set => currentFlowEnvironment.InfoClass = value; }
|
||||
public RunState FlowState { get => currentFlowEnvironment.FlowState; set => currentFlowEnvironment.FlowState = value; }
|
||||
public RunState FlipFlopState { get => currentFlowEnvironment.FlipFlopState; set => currentFlowEnvironment.FlipFlopState = value; }
|
||||
//public RunState FlipFlopState { get => currentFlowEnvironment.FlipFlopState; set => currentFlowEnvironment.FlipFlopState = value; }
|
||||
|
||||
public event LoadDllHandler OnDllLoad {
|
||||
add { currentFlowEnvironmentEvent.OnDllLoad += value; }
|
||||
@@ -607,10 +607,10 @@ namespace Serein.NodeFlow.Env
|
||||
return IOC.RegisterPersistennceInstance(key, instance);
|
||||
}
|
||||
|
||||
public bool RegisterInstance(string key, object instance)
|
||||
{
|
||||
return IOC.RegisterInstance(key, instance);
|
||||
}
|
||||
//public bool RegisterInstance(string key, object instance)
|
||||
//{
|
||||
// return IOC.RegisterInstance(key, instance);
|
||||
//}
|
||||
|
||||
public object Get(Type type)
|
||||
{
|
||||
@@ -622,10 +622,10 @@ namespace Serein.NodeFlow.Env
|
||||
return IOC.Get<T>();
|
||||
}
|
||||
|
||||
public T Get<T>(string key)
|
||||
{
|
||||
return IOC.Get<T>(key);
|
||||
}
|
||||
//public T Get<T>(string key)
|
||||
//{
|
||||
// return IOC.Get<T>(key);
|
||||
//}
|
||||
|
||||
public object Instantiate(Type type)
|
||||
{
|
||||
|
||||
@@ -784,7 +784,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
//MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息
|
||||
var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点
|
||||
var nodeModel = FlowNodeExtension.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点
|
||||
nodeModel.LoadInfo(nodeInfo);
|
||||
TryAddNode(nodeModel);
|
||||
IsLoadingNode = false;
|
||||
@@ -1098,7 +1098,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
#endregion
|
||||
|
||||
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||
var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||
if (nodeModel is null)
|
||||
{
|
||||
nodeInfo.Guid = string.Empty;
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 流程环境需要的扩展方法
|
||||
/// </summary>
|
||||
public static class FlowFunc
|
||||
public static class FlowNodeExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 判断是否为基础节点
|
||||
@@ -71,37 +71,6 @@ namespace Serein.NodeFlow
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 从节点信息读取节点类型
|
||||
///// </summary>
|
||||
///// <param name="nodeInfo"></param>
|
||||
///// <returns></returns>
|
||||
///// <exception cref="NotImplementedException"></exception>
|
||||
//public static NodeControlType GetNodeControlType(NodeInfo nodeInfo)
|
||||
//{
|
||||
// if(!EnumHelper.TryConvertEnum<NodeControlType>(nodeInfo.Type, out var controlType))
|
||||
// {
|
||||
// return NodeControlType.None;
|
||||
// }
|
||||
// return controlType;
|
||||
// // 创建控件实例
|
||||
// //NodeControlType controlType = nodeInfo.Type switch
|
||||
// //{
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件
|
||||
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件
|
||||
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
|
||||
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleGlobalDataNode)}" => NodeControlType.GlobalData, // 数据节点
|
||||
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleScriptNode)}" => NodeControlType.Script, // 数据节点
|
||||
// // _ => NodeControlType.None,
|
||||
// //};
|
||||
// //return controlType;
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 程序集封装依赖
|
||||
/// </summary>
|
||||
@@ -1,413 +0,0 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System.Collections.Concurrent;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程启动器
|
||||
/// </summary>
|
||||
public class FlowStarter
|
||||
{
|
||||
/// <summary>
|
||||
/// 控制所有全局触发器的结束
|
||||
/// </summary>
|
||||
private CancellationTokenSource? _flipFlopCts;
|
||||
|
||||
/// <summary>
|
||||
/// 是否停止启动
|
||||
/// </summary>
|
||||
private bool IsStopStart = false;
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行时需要执行的方法
|
||||
/// </summary>
|
||||
private Func<Task>? ExitAction { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="env"></param>
|
||||
/// <param name="startNode"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode)
|
||||
{
|
||||
IDynamicContext context;
|
||||
context = new Serein.Library.DynamicContext(env); // 从起始节点启动流程时创建上下文
|
||||
await startNode.StartFlowAsync(context); // 开始运行时从选定节点开始运行
|
||||
context.Exit();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始运行(需要准备好方法信息)
|
||||
/// </summary>
|
||||
/// <param name="env">运行环境</param>
|
||||
/// <param name="nodes">环境中已加载的所有节点</param>
|
||||
/// <param name="initMethods">初始化方法</param>
|
||||
/// <param name="loadingMethods">加载时方法</param>
|
||||
/// <param name="exitMethods">结束时方法</param>
|
||||
/// <returns></returns>
|
||||
public async Task RunAsync(IFlowEnvironment env,
|
||||
List<NodeModelBase> nodes,
|
||||
Dictionary<RegisterSequence, List<Type>> autoRegisterTypes,
|
||||
List<MethodDetails> initMethods,
|
||||
List<MethodDetails> loadingMethods,
|
||||
List<MethodDetails> exitMethods)
|
||||
{
|
||||
|
||||
#region 注册基本类
|
||||
env.IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
||||
#endregion
|
||||
|
||||
env.FlowState = RunState.Running; // 开始运行
|
||||
NodeModelBase? startNode = nodes.FirstOrDefault(node => node.IsStart);
|
||||
if (startNode is null) {
|
||||
env.FlowState = RunState.Completion; // 不存在起点,退出流程
|
||||
return;
|
||||
}
|
||||
|
||||
#region 获取所有触发器,以及已加载节点的方法信息
|
||||
List<MethodDetails> runNodeMd;
|
||||
List<SingleFlipflopNode> flipflopNodes;
|
||||
|
||||
flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == NodeType.Flipflop && it.IsStart == false)
|
||||
.Select(it => (SingleFlipflopNode)it)
|
||||
.Where(node => node.DebugSetting.IsEnable && node is SingleFlipflopNode flipflopNode && flipflopNode.NotExitPreviousNode())
|
||||
.ToList();// 获取需要再运行开始之前启动的触发器节点
|
||||
runNodeMd = nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 选择运行环境的上下文
|
||||
|
||||
// 判断使用哪一种流程上下文
|
||||
IDynamicContext Context = new Serein.Library.DynamicContext(env); // 从起始节点启动流程时创建上下文
|
||||
#endregion
|
||||
|
||||
#region 初始化运行环境的Ioc容器
|
||||
|
||||
// 清除节点使用的对象,筛选出需要初始化的方法描述
|
||||
var thisRuningMds = new List<MethodDetails>();
|
||||
thisRuningMds.AddRange(runNodeMd.Where(md => md?.ActingInstanceType is not null));
|
||||
thisRuningMds.AddRange(initMethods.Where(md => md?.ActingInstanceType is not null));
|
||||
thisRuningMds.AddRange(loadingMethods.Where(md => md?.ActingInstanceType is not null));
|
||||
thisRuningMds.AddRange(exitMethods.Where(md => md?.ActingInstanceType is not null));
|
||||
|
||||
|
||||
foreach (var nodeMd in thisRuningMds)
|
||||
{
|
||||
nodeMd.ActingInstance = null;
|
||||
}
|
||||
|
||||
// 初始化ioc容器中的类型对象
|
||||
foreach (var md in thisRuningMds)
|
||||
{
|
||||
if (md.ActingInstanceType != null)
|
||||
{
|
||||
env.IOC.Register(md.ActingInstanceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"{md.MethodName} - 没有类型声明");
|
||||
IsStopStart = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopStart) return;// 检查所有dll节点是否存在类型
|
||||
|
||||
env.IOC.Build(); // 流程启动前的初始化
|
||||
|
||||
foreach (var md in thisRuningMds)
|
||||
{
|
||||
md.ActingInstance = env.IOC.Get(md.ActingInstanceType);
|
||||
if(md.ActingInstance is null)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"{md.MethodName} - 无法获取类型[{md.ActingInstanceType}]的实例");
|
||||
IsStopStart = true;
|
||||
}
|
||||
}
|
||||
if (IsStopStart)
|
||||
{
|
||||
return;// 调用节点初始化后检查状态
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 执行初始化,绑定IOC容器,再执行加载时
|
||||
|
||||
if (autoRegisterTypes.TryGetValue(RegisterSequence.FlowInit, out var flowInitTypes))
|
||||
{
|
||||
foreach (var type in flowInitTypes)
|
||||
{
|
||||
env.IOC.Register(type); // 初始化前注册
|
||||
}
|
||||
}
|
||||
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
|
||||
//object?[]? args = [Context];
|
||||
foreach (var md in initMethods) // 初始化
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
await dd.InvokeAsync(md.ActingInstance, [Context]);
|
||||
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||
}
|
||||
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
|
||||
|
||||
if(autoRegisterTypes.TryGetValue(RegisterSequence.FlowLoading,out var flowLoadingTypes))
|
||||
{
|
||||
foreach (var type in flowLoadingTypes)
|
||||
{
|
||||
env.IOC.Register(type); // 初始化前注册
|
||||
}
|
||||
}
|
||||
Context.Env.IOC.Build(); // 绑定初始化时注册的类型
|
||||
foreach (var md in loadingMethods) // 加载
|
||||
{
|
||||
//object?[]? data = [md.ActingInstance, args];
|
||||
//md.MethodDelegate.DynamicInvoke(data);
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行正在加载
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
await dd.InvokeAsync(md.ActingInstance, [Context]);
|
||||
//((Action<object, object?[]?>)del).Invoke(md.ActingInstance, [Context]);
|
||||
//((Func<object, object[], object>)dd.EmitDelegate).Invoke(md.ActingInstance, [Context]);
|
||||
}
|
||||
Context.Env.IOC.Build(); // 预防有人在加载时才注册类型,再绑定一次
|
||||
#endregion
|
||||
|
||||
#region 设置流程退出时的回调函数
|
||||
ExitAction = async () =>
|
||||
{
|
||||
//env.IOC.Run<WebApiServer>(web => {
|
||||
// web?.Stop();
|
||||
//});
|
||||
//env.IOC.Run<WebSocketServer>(server => {
|
||||
// server?.Stop();
|
||||
//});
|
||||
|
||||
foreach (MethodDetails? md in exitMethods)
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行退出执行
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
await dd.InvokeAsync(md.ActingInstance, [Context]);
|
||||
}
|
||||
|
||||
if (_flipFlopCts != null && !_flipFlopCts.IsCancellationRequested)
|
||||
{
|
||||
_flipFlopCts?.Cancel();
|
||||
_flipFlopCts?.Dispose();
|
||||
} // 通知所有流程上下文停止运行
|
||||
TerminateAllGlobalFlipflop(); // 确保所有触发器不再运行
|
||||
SereinEnv.ClearFlowGlobalData(); // 清空全局数据缓存
|
||||
NativeDllHelper.FreeLibrarys(); // 卸载所有已加载的 Native Dll
|
||||
env.IOC.Run<FlowInterruptTool>(fit => fit.CancelAllTrigger());// 取消所有中断
|
||||
env.FlowState = RunState.Completion;
|
||||
env.FlipFlopState = RunState.Completion;
|
||||
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region 开始启动流程
|
||||
|
||||
try
|
||||
{
|
||||
//await TestScript(env);
|
||||
await startNode.StartFlowAsync(Context); // 开始运行时从起始节点开始运行
|
||||
|
||||
if (flipflopNodes.Count > 0)
|
||||
{
|
||||
env.FlipFlopState = RunState.Running;
|
||||
// 如果存在需要启动的触发器,则开始启动
|
||||
_flipFlopCts = new CancellationTokenSource();
|
||||
env.IOC.RegisterInstance(NodeStaticConfig.FlipFlopCtsName, _flipFlopCts);
|
||||
|
||||
// 使用 TaskCompletionSource 创建未启动的触发器任务
|
||||
var tasks = flipflopNodes.Select(async node =>
|
||||
{
|
||||
await RunGlobalFlipflopAsync(env,node); // 启动流程时启动全局触发器
|
||||
}).ToArray();
|
||||
_ = Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
|
||||
// 等待结束
|
||||
if(env.FlipFlopState == RunState.Running && _flipFlopCts is not null)
|
||||
{
|
||||
while (!_flipFlopCts.IsCancellationRequested)
|
||||
{
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync(ex.ToString());
|
||||
}
|
||||
finally
|
||||
{
|
||||
env.FlowState = RunState.Completion;
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}");;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
|
||||
|
||||
/// <summary>
|
||||
/// 尝试添加全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode"></param>
|
||||
/// <param name="env"></param>
|
||||
public async Task RunGlobalFlipflopAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode)
|
||||
{
|
||||
if (dictGlobalFlipflop.TryAdd(singleFlipFlopNode, new CancellationTokenSource()))
|
||||
{
|
||||
singleFlipFlopNode.MethodDetails.ActingInstance ??= env.IOC.Get(singleFlipFlopNode.MethodDetails.ActingInstanceType);
|
||||
await FlipflopExecuteAsync(env, singleFlipFlopNode, dictGlobalFlipflop[singleFlipFlopNode]);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试移除全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode"></param>
|
||||
public void TerminateGlobalFlipflopRuning(SingleFlipflopNode singleFlipFlopNode)
|
||||
{
|
||||
if (dictGlobalFlipflop.TryRemove(singleFlipFlopNode, out var cts))
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
cts.Cancel();
|
||||
}
|
||||
cts.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 终结所有全局触发器
|
||||
/// </summary>
|
||||
private void TerminateAllGlobalFlipflop()
|
||||
{
|
||||
foreach ((var node, var cts) in dictGlobalFlipflop)
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
cts.Cancel();
|
||||
}
|
||||
cts.Dispose();
|
||||
}
|
||||
dictGlobalFlipflop.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动全局触发器
|
||||
/// </summary>
|
||||
/// <param name="env">流程运行全局环境</param>
|
||||
/// <param name="singleFlipFlopNode">需要全局监听信号的触发器</param>
|
||||
/// <returns></returns>
|
||||
private async Task FlipflopExecuteAsync(IFlowEnvironment env,
|
||||
SingleFlipflopNode singleFlipFlopNode,
|
||||
CancellationTokenSource cts)
|
||||
{
|
||||
if(_flipFlopCts is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "流程尚未启动,flowStarter尚未创建,无法启动该节点");
|
||||
return;
|
||||
}
|
||||
|
||||
while (!_flipFlopCts.IsCancellationRequested && !cts.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = new Library.DynamicContext(env); // 启动全局触发器时新建上下文
|
||||
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
|
||||
context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData);
|
||||
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
|
||||
if (context.NextOrientation == ConnectionInvokeType.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_ = Task.Run(async () => {
|
||||
var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation];
|
||||
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
|
||||
{
|
||||
// 筛选出启用的节点
|
||||
if (!nextNodes[i].DebugSetting.IsEnable)
|
||||
{
|
||||
continue ;
|
||||
}
|
||||
|
||||
context.SetPreviousNode(nextNodes[i], singleFlipFlopNode);
|
||||
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前
|
||||
{
|
||||
await nextNodes[i].DebugSetting.GetInterruptTask.Invoke();
|
||||
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已取消,开始执行后继分支");
|
||||
}
|
||||
await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点
|
||||
}
|
||||
|
||||
nextNodes = singleFlipFlopNode.SuccessorNodes[ConnectionInvokeType.Upstream];
|
||||
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
|
||||
{
|
||||
// 筛选出启用的节点
|
||||
if (!nextNodes[i].DebugSetting.IsEnable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
context.SetPreviousNode(nextNodes[i], singleFlipFlopNode);
|
||||
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前
|
||||
{
|
||||
await nextNodes[i].DebugSetting.GetInterruptTask.Invoke();
|
||||
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已取消,开始执行后继分支");
|
||||
}
|
||||
await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点
|
||||
}
|
||||
|
||||
context.Exit();
|
||||
});
|
||||
|
||||
}
|
||||
catch (FlipflopException ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
|
||||
if (ex.Type == FlipflopException.CancelClass.CancelFlow)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.Guid}]异常。"+ ex.Message);
|
||||
await Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束流程
|
||||
/// </summary>
|
||||
public void Exit()
|
||||
{
|
||||
ExitAction?.Invoke();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
52
NodeFlow/FlowTaskLibrary.cs
Normal file
52
NodeFlow/FlowTaskLibrary.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
|
||||
public class FlowTaskLibrary()
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程运行环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment Environment { get; set; }// = environment;
|
||||
|
||||
/// <summary>
|
||||
/// 表示运行环境状态
|
||||
/// </summary>
|
||||
public CancellationTokenSource CancellationTokenSource { get; } = new CancellationTokenSource();
|
||||
|
||||
/// <summary>
|
||||
/// 上下文线程池
|
||||
/// </summary>
|
||||
public Serein.Library.Utils.ObjectPool<IDynamicContext> FlowContextPool { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 当前任务加载的所有节点
|
||||
/// </summary>
|
||||
public List<NodeModelBase> Nodes { get; set; }// = nodes;
|
||||
/// <summary>
|
||||
/// 需要注册的类型
|
||||
/// </summary>
|
||||
public Dictionary<RegisterSequence, List<Type>> AutoRegisterTypes { get; set; } //= autoRegisterTypes;
|
||||
/// <summary>
|
||||
/// 初始化时需要的方法
|
||||
/// </summary>
|
||||
public List<MethodDetails> InitMds { get; set; }// = initMds;
|
||||
/// <summary>
|
||||
/// 加载时需要的方法
|
||||
/// </summary>
|
||||
public List<MethodDetails> LoadMds { get; set; }// = loadMds;
|
||||
/// <summary>
|
||||
/// 退出时需要调用的方法
|
||||
/// </summary>
|
||||
public List<MethodDetails> ExitMds { get; set; } //= exitMds;
|
||||
}
|
||||
|
||||
}
|
||||
386
NodeFlow/FlowWorkManagement.cs
Normal file
386
NodeFlow/FlowWorkManagement.cs
Normal file
@@ -0,0 +1,386 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程任务管理
|
||||
/// </summary>
|
||||
public class FlowWorkManagement
|
||||
{
|
||||
/// <summary>
|
||||
/// 触发器对应的Cts
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行时需要执行的方法
|
||||
/// </summary>
|
||||
private Func<Task>? ExitAction { get; set; }
|
||||
/// <summary>
|
||||
/// 初始化选项
|
||||
/// </summary>
|
||||
public FlowTaskLibrary WorkLibrary { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 流程任务管理
|
||||
/// </summary>
|
||||
/// <param name="library"></param>
|
||||
public FlowWorkManagement(FlowTaskLibrary library)
|
||||
{
|
||||
WorkLibrary = library;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化啊
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> RunAsync(CancellationToken token)
|
||||
{
|
||||
NodeModelBase? startNode = WorkLibrary.Nodes.FirstOrDefault(node => node.IsStart);
|
||||
if (startNode is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!RegisterAllType())
|
||||
{
|
||||
return false;
|
||||
};
|
||||
var initState = await TryInit();
|
||||
if (!initState)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
var loadState = await TryLoadAsync();
|
||||
if (!loadState)
|
||||
{
|
||||
return false;
|
||||
};
|
||||
var task = CallFlipflopNode();
|
||||
await CallStartNode(startNode);
|
||||
await task;
|
||||
await CallExit();
|
||||
return true;
|
||||
}
|
||||
|
||||
#region 初始化
|
||||
private bool RegisterAllType()
|
||||
{
|
||||
var env = WorkLibrary.Environment;
|
||||
var nodeMds = WorkLibrary.Nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息
|
||||
var allMds = new List<MethodDetails>();
|
||||
allMds.AddRange(nodeMds.Where(md => md?.ActingInstanceType is not null));
|
||||
allMds.AddRange(WorkLibrary.InitMds.Where(md => md?.ActingInstanceType is not null));
|
||||
allMds.AddRange(WorkLibrary.LoadMds.Where(md => md?.ActingInstanceType is not null));
|
||||
allMds.AddRange(WorkLibrary.ExitMds.Where(md => md?.ActingInstanceType is not null));
|
||||
var isSuccessful = true;
|
||||
foreach (var md in allMds)
|
||||
{
|
||||
if (md.ActingInstanceType != null)
|
||||
{
|
||||
env.IOC.Register(md.ActingInstanceType);
|
||||
}
|
||||
else
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, "{md.MethodName} - 没有类型声明");
|
||||
isSuccessful = false ;
|
||||
}
|
||||
}
|
||||
env.IOC.Build(); // 绑定初始化时注册的类型
|
||||
foreach (var md in allMds)
|
||||
{
|
||||
var instance = env.IOC.Get(md.ActingInstanceType);
|
||||
if (instance is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"{md.MethodName} - 无法获取类型[{md.ActingInstanceType}]的实例");
|
||||
isSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
private async Task<bool> TryInit()
|
||||
{
|
||||
var env = WorkLibrary.Environment;
|
||||
var initMds = WorkLibrary.InitMds;
|
||||
var pool = WorkLibrary.FlowContextPool;
|
||||
var ioc = WorkLibrary.Environment.IOC;
|
||||
foreach (var md in initMds) // 初始化
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
var context = pool.Allocate();
|
||||
var instance = ioc.Get(md.ActingInstanceType);
|
||||
await dd.InvokeAsync(instance, [context]);
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
env.IOC.Build(); // 绑定初始化时注册的类型
|
||||
var isSuccessful = true;
|
||||
return isSuccessful;
|
||||
}
|
||||
private async Task<bool> TryLoadAsync()
|
||||
{
|
||||
var env = WorkLibrary.Environment;
|
||||
var loadMds = WorkLibrary.LoadMds;
|
||||
var pool = WorkLibrary.FlowContextPool;
|
||||
var ioc = WorkLibrary.Environment.IOC;
|
||||
foreach (var md in loadMds) // 加载时
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
var context = pool.Allocate();
|
||||
var instance = ioc.Get(md.ActingInstanceType);
|
||||
await dd.InvokeAsync(instance, [context]);
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
env.IOC.Build(); // 绑定初始化时注册的类型
|
||||
var isSuccessful = true;
|
||||
return isSuccessful;
|
||||
|
||||
}
|
||||
private async Task<bool> CallExit()
|
||||
{
|
||||
var env = WorkLibrary.Environment;
|
||||
var mds = WorkLibrary.ExitMds;
|
||||
var pool = WorkLibrary.FlowContextPool;
|
||||
var ioc = WorkLibrary.Environment.IOC;
|
||||
|
||||
ioc.Run<FlowInterruptTool>(fit => fit.CancelAllTrigger());// 取消所有中断
|
||||
foreach (var md in mds) // 结束时
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
var context = pool.Allocate();
|
||||
var instance = ioc.Get(md.ActingInstanceType);
|
||||
await dd.InvokeAsync(instance, [context]);
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
|
||||
TerminateAllGlobalFlipflop(); // 确保所有触发器不再运行
|
||||
SereinEnv.ClearFlowGlobalData(); // 清空全局数据缓存
|
||||
NativeDllHelper.FreeLibrarys(); // 卸载所有已加载的 Native Dll
|
||||
|
||||
var isSuccessful = true;
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
private Task CallFlipflopNode()
|
||||
{
|
||||
var env = WorkLibrary.Environment;
|
||||
var flipflopNodes = WorkLibrary.Nodes.Where(item => item is SingleFlipflopNode node
|
||||
&& !node.IsStart
|
||||
&& node.DebugSetting.IsEnable
|
||||
&& node.NotExitPreviousNode())
|
||||
.Select(item => (SingleFlipflopNode)item);
|
||||
//.ToList();// 获取需要再运行开始之前启动的触发器节点
|
||||
|
||||
if (flipflopNodes.Count() > 0)
|
||||
{
|
||||
var tasks = flipflopNodes.Select(async node =>
|
||||
{
|
||||
await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器
|
||||
});
|
||||
Task.WhenAll(tasks);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
private async Task CallStartNode(NodeModelBase startNode)
|
||||
{
|
||||
var pool = WorkLibrary.FlowContextPool;
|
||||
var token = WorkLibrary.CancellationTokenSource.Token;
|
||||
var context = pool.Allocate();
|
||||
await startNode.StartFlowAsync(context, token);
|
||||
context.Exit();
|
||||
pool.Free(context);
|
||||
return;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="env"></param>
|
||||
/// <param name="startNode"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode)
|
||||
{
|
||||
var pool = WorkLibrary.FlowContextPool;
|
||||
var context = pool.Allocate();
|
||||
var token = WorkLibrary.CancellationTokenSource.Token;
|
||||
await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 尝试添加全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode"></param>
|
||||
/// <param name="env"></param>
|
||||
public async Task RunGlobalFlipflopAsync(IFlowEnvironment env, SingleFlipflopNode singleFlipFlopNode)
|
||||
{
|
||||
if (dictGlobalFlipflop.TryAdd(singleFlipFlopNode, new CancellationTokenSource()))
|
||||
{
|
||||
var cts = dictGlobalFlipflop[singleFlipFlopNode];
|
||||
await FlipflopExecuteAsync(singleFlipFlopNode, cts.Token);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 尝试移除全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode"></param>
|
||||
public void TerminateGlobalFlipflopRuning(SingleFlipflopNode singleFlipFlopNode)
|
||||
{
|
||||
if (dictGlobalFlipflop.TryRemove(singleFlipFlopNode, out var cts))
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
cts.Cancel();
|
||||
}
|
||||
cts.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 终结所有全局触发器
|
||||
/// </summary>
|
||||
private void TerminateAllGlobalFlipflop()
|
||||
{
|
||||
foreach ((var node, var cts) in dictGlobalFlipflop)
|
||||
{
|
||||
if (!cts.IsCancellationRequested)
|
||||
{
|
||||
cts.Cancel();
|
||||
}
|
||||
cts.Dispose();
|
||||
}
|
||||
dictGlobalFlipflop.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启动全局触发器
|
||||
/// </summary>
|
||||
/// <param name="singleFlipFlopNode">需要全局监听信号的触发器</param>
|
||||
/// <param name="singleToken">单个触发器持有的</param>
|
||||
/// <returns></returns>
|
||||
private async Task FlipflopExecuteAsync(SingleFlipflopNode singleFlipFlopNode,
|
||||
CancellationToken singleToken)
|
||||
{
|
||||
|
||||
var pool = WorkLibrary.FlowContextPool;
|
||||
while (!singleToken.IsCancellationRequested && !singleToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
var context = pool.Allocate(); // 启动全局触发器时新建上下文
|
||||
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context, singleToken); // 获取触发器等待Task
|
||||
context.AddOrUpdate(singleFlipFlopNode.Guid, newFlowData);
|
||||
if (context.NextOrientation == ConnectionInvokeType.None)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
_ = Task.Run(() => CallSubsequentNode(singleFlipFlopNode, singleToken, pool, context)); // 重新启动触发器
|
||||
|
||||
}
|
||||
catch (FlipflopException ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
|
||||
if (ex.Type == FlipflopException.CancelClass.CancelFlow)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.Guid}]异常。"+ ex.Message);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static async Task? CallSubsequentNode(SingleFlipflopNode singleFlipFlopNode, CancellationToken singleToken, ObjectPool<IDynamicContext> pool, IDynamicContext context)
|
||||
{
|
||||
var flowState = context.NextOrientation; // 记录一下流程状态
|
||||
var nextNodes = singleFlipFlopNode.SuccessorNodes[ConnectionInvokeType.Upstream]; // 优先调用上游分支
|
||||
for (int i = nextNodes.Count - 1; i >= 0 && !singleToken.IsCancellationRequested; i--)
|
||||
{
|
||||
// 筛选出启用的节点
|
||||
if (!nextNodes[i].DebugSetting.IsEnable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); // 设置调用关系
|
||||
|
||||
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前检查终端
|
||||
{
|
||||
await nextNodes[i].DebugSetting.GetInterruptTask.Invoke();
|
||||
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已取消,开始执行后继分支");
|
||||
}
|
||||
await nextNodes[i].StartFlowAsync(context, singleToken); // 启动执行触发器后继分支的节点
|
||||
}
|
||||
|
||||
nextNodes = singleFlipFlopNode.SuccessorNodes[flowState]; // 调用对应分支
|
||||
for (int i = nextNodes.Count - 1; i >= 0 && !singleToken.IsCancellationRequested; i--)
|
||||
{
|
||||
// 筛选出启用的节点
|
||||
if (!nextNodes[i].DebugSetting.IsEnable)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
context.SetPreviousNode(nextNodes[i], singleFlipFlopNode);
|
||||
if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前
|
||||
{
|
||||
await nextNodes[i].DebugSetting.GetInterruptTask.Invoke();
|
||||
await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已取消,开始执行后继分支");
|
||||
}
|
||||
await nextNodes[i].StartFlowAsync(context, singleToken); // 启动执行触发器后继分支的节点
|
||||
}
|
||||
|
||||
context.Reset();
|
||||
pool.Free(context);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束流程
|
||||
/// </summary>
|
||||
public void Exit()
|
||||
{
|
||||
ExitAction?.Invoke();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,14 +36,18 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 条件区域中遍历每个条件节点
|
||||
foreach (SingleConditionNode? node in ConditionNodes)
|
||||
{
|
||||
var state = await node.ExecutingAsync(context);
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
var state = await node.ExecutingAsync(context, token);
|
||||
if (context.NextOrientation != ConnectionInvokeType.IsSucceed)
|
||||
{
|
||||
// 如果条件不通过,立刻推出循环
|
||||
|
||||
@@ -110,8 +110,9 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested) return null;
|
||||
// 接收上一节点参数or自定义参数内容
|
||||
object? parameter;
|
||||
object? result = null;
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Serein.Library;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
@@ -91,8 +92,10 @@ namespace Serein.NodeFlow.Model
|
||||
}
|
||||
|
||||
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
if(token.IsCancellationRequested) return null;
|
||||
|
||||
object? parameter = null;// context.TransmissionData(this); // 表达式节点使用上一节点数据
|
||||
var pd = MethodDetails.ParameterDetailss[0];
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
|
||||
namespace Serein.NodeFlow.Model
|
||||
{
|
||||
@@ -21,7 +22,7 @@ namespace Serein.NodeFlow.Model
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
#region 执行前中断
|
||||
if (DebugSetting.IsInterrupt) // 执行触发前
|
||||
@@ -37,13 +38,18 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
object instance = md.ActingInstance;
|
||||
|
||||
var args = await GetParametersAsync(context);
|
||||
var instance = context.Env.IOC.Get(md.ActingInstanceType);
|
||||
await dd.InvokeAsync(instance, [context]);
|
||||
var args = await GetParametersAsync(context, token);
|
||||
// 因为这里会返回不确定的泛型 IFlipflopContext<TRsult>
|
||||
// 而我们只需要获取到 State 和 Value(返回的数据)
|
||||
// 所以使用 dynamic 类型接收
|
||||
dynamic dynamicFlipflopContext = await dd.InvokeAsync(md.ActingInstance, args);
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
dynamic dynamicFlipflopContext = await dd.InvokeAsync(instance, args);
|
||||
FlipflopStateType flipflopStateType = dynamicFlipflopContext.State;
|
||||
context.NextOrientation = flipflopStateType.ToContentType();
|
||||
|
||||
|
||||
@@ -115,8 +115,9 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested) return null;
|
||||
if (string.IsNullOrEmpty(KeyName))
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
|
||||
@@ -165,13 +165,15 @@ namespace Serein.NodeFlow.Model
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <returns></returns>
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
var @params = await GetParametersAsync(context);
|
||||
|
||||
if(token.IsCancellationRequested) return null;
|
||||
var @params = await GetParametersAsync(context, token);
|
||||
if(token.IsCancellationRequested) return null;
|
||||
|
||||
|
||||
//context.AddOrUpdate($"{context.Guid}_{this.Guid}_Params", @params[0]); // 后面再改
|
||||
ReloadScript();// 每次都重新解析
|
||||
ReloadScript();// 每次都重新解析
|
||||
|
||||
IScriptInvokeContext scriptContext = new ScriptInvokeContext(context);
|
||||
|
||||
@@ -193,6 +195,9 @@ namespace Serein.NodeFlow.Model
|
||||
|
||||
var envEvent = (IFlowEnvironmentEvent)context.Env;
|
||||
envEvent.OnFlowRunComplete += onFlowStop; // 防止运行后台流程
|
||||
|
||||
if (token.IsCancellationRequested) return null;
|
||||
|
||||
var result = await ScriptInterpreter.InterpretAsync(scriptContext, mainNode); // 从入口节点执行
|
||||
envEvent.OnFlowRunComplete -= onFlowStop;
|
||||
//SereinEnv.WriteLine(InfoType.INFO, "FlowContext Guid : " + context.Guid);
|
||||
|
||||
@@ -15,12 +15,13 @@ namespace Serein.NodeFlow.Model
|
||||
{
|
||||
}
|
||||
|
||||
public override async Task<object> ExecutingAsync(IDynamicContext context)
|
||||
public override async Task<object?> ExecutingAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested) return null;
|
||||
if(Adapter is null)
|
||||
{
|
||||
|
||||
var result = await base.ExecutingAsync(context);
|
||||
var result = await base.ExecutingAsync(context, token);
|
||||
if (result is IEmbeddedContent adapter)
|
||||
{
|
||||
this.Adapter = adapter;
|
||||
@@ -39,7 +40,7 @@ namespace Serein.NodeFlow.Model
|
||||
iflowContorl.OnExecuting(data);
|
||||
}
|
||||
|
||||
return Task.FromResult<object?>(null);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,8 +3,10 @@
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Serein.Workbench"
|
||||
xmlns:view="clr-namespace:Serein.Workbench.Views"
|
||||
StartupUri="Views/FlowWorkbenchView.xaml"
|
||||
StartupUri="MainWindow.xaml"
|
||||
Startup="Application_Startup">
|
||||
<!--StartupUri="Views/FlowWorkbenchView.xaml"-->
|
||||
|
||||
<Application.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
|
||||
@@ -90,17 +90,19 @@ namespace Serein.Workbench
|
||||
|
||||
public App()
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(500);
|
||||
await this.LoadLocalProjectAsync();
|
||||
});
|
||||
return;
|
||||
var collection = new ServiceCollection();
|
||||
collection.AddWorkbenchServices();
|
||||
collection.AddFlowServices();
|
||||
collection.AddViewModelServices();
|
||||
var services = collection.BuildServiceProvider(); // 绑定并返回获取实例的服务接口
|
||||
App.ServiceProvider = services;
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(500);
|
||||
await this.LoadLocalProjectAsync();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -115,14 +117,14 @@ namespace Serein.Workbench
|
||||
filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\net8.0\PLCproject.dnf";
|
||||
filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf";
|
||||
filePath = @"F:\临时\project\project.dnf";
|
||||
filePath = @"F:\临时\flow\qrcode\project.dnf";
|
||||
filePath = @"F:\TempFile\flow\qrcode\project.dnf";
|
||||
//filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\test.dnf";
|
||||
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
||||
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
|
||||
var dir = Path.GetDirectoryName(filePath);
|
||||
|
||||
App.GetService<IFlowEnvironment>().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData },App.FileDataPath);
|
||||
//App.GetService<IFlowEnvironment>().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData },App.FileDataPath);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -29,7 +29,9 @@ namespace Serein.Workbench.Node.ViewModel
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await NodeModel.ExecutingAsync(new Library.DynamicContext(nodeModel.Env));
|
||||
var cts = new CancellationTokenSource();
|
||||
var result = await NodeModel.ExecutingAsync(new Library.DynamicContext(nodeModel.Env), cts.Token);
|
||||
cts.Cancel();
|
||||
SereinEnv.WriteLine(InfoType.INFO, result?.ToString());
|
||||
}
|
||||
catch (Exception ex)
|
||||
|
||||
@@ -25,7 +25,9 @@ namespace Serein.Workbench.Node.ViewModel
|
||||
Task.Factory.StartNew(async () =>
|
||||
{
|
||||
var context = new DynamicContext(NodeModel.Env);
|
||||
await NodeModel.ExecutingAsync(context);
|
||||
var cts = new CancellationTokenSource();
|
||||
await NodeModel.ExecutingAsync(context, cts.Token);
|
||||
cts?.Dispose();
|
||||
if (context.NextOrientation == ConnectionInvokeType.IsSucceed
|
||||
&& NodeModel.Adapter.GetUserControl() is UserControl userControl)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user