mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-02 22:36:35 +08:00
1. 脚本转c#代码功能,支持了[Flipflop]触发器节点
2. 修复了Script.StringNode转C#中存在多余的转义符的问题 3. 为IFlowControl添加了Task StratNodeAsync(string)的接口,用于在代码生成场景中的流程控制 4. 调整了关于Lightweight运行环境的文件位置
This commit is contained in:
@@ -40,6 +40,13 @@ namespace Serein.Library.Api
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
Task<TResult> StartFlowAsync<TResult>(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
Task StartFlowAsync(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行
|
||||
|
||||
@@ -1,10 +1,5 @@
|
||||
|
||||
|
||||
using Serein.Library.FlowNode;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Api
|
||||
|
||||
332
Library/FlowNode/Env/CallNode.cs
Normal file
332
Library/FlowNode/Env/CallNode.cs
Normal file
@@ -0,0 +1,332 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 调用节点,代表一个流程中的调用点,可以是一个Action或一个异步函数。
|
||||
/// </summary>
|
||||
|
||||
public class CallNode
|
||||
{
|
||||
|
||||
private Func<IFlowContext, Task> taskFunc;
|
||||
private Action<IFlowContext> action;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的调用节点,使用指定的节点Guid。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
public CallNode(string nodeGuid)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的调用节点,使用指定的节点Guid和Action。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="action"></param>
|
||||
public CallNode(string nodeGuid, Action<IFlowContext> action)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.action = action;
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的调用节点,使用指定的节点Guid和异步函数。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="func"></param>
|
||||
public CallNode(string nodeGuid, Func<IFlowContext, Task> func)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.taskFunc = func;
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化调用节点,设置默认的子节点和后继节点字典。
|
||||
/// </summary>
|
||||
private void Init()
|
||||
{
|
||||
//PreviousNodes = new Dictionary<ConnectionInvokeType, List<CallNode>>();
|
||||
SuccessorNodes = new Dictionary<ConnectionInvokeType, List<CallNode>>();
|
||||
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
//PreviousNodes[ctType] = new List<CallNode>();
|
||||
SuccessorNodes[ctType] = new List<CallNode>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum ActionType
|
||||
{
|
||||
Action,
|
||||
Task,
|
||||
}
|
||||
private ActionType actionType = ActionType.Action;
|
||||
|
||||
/// <summary>
|
||||
/// 设置调用节点的Action,表示该节点执行一个同步操作。
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void SetAction(Action<IFlowContext> action)
|
||||
{
|
||||
this.action = action;
|
||||
actionType = ActionType.Action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置调用节点的异步函数,表示该节点执行一个异步操作。
|
||||
/// </summary>
|
||||
/// <param name="taskFunc"></param>
|
||||
public void SetAction(Func<IFlowContext, Task> taskFunc)
|
||||
{
|
||||
this.taskFunc = taskFunc;
|
||||
actionType = ActionType.Task;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 对应的节点
|
||||
/// </summary>
|
||||
public string Guid { get; }
|
||||
|
||||
#if false
|
||||
|
||||
/// <summary>
|
||||
/// 不同分支的父节点(流程调用)
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionInvokeType, List<CallNode>> PreviousNodes { get; private set; }
|
||||
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 不同分支的子节点(流程调用)
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionInvokeType, List<CallNode>> SuccessorNodes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子节点数组,分为四个分支:上游、成功、失败、错误,每个分支最多支持16个子节点。
|
||||
/// </summary>
|
||||
public CallNode[][] ChildNodes { get; private set; } = new CallNode[][]
|
||||
{
|
||||
new CallNode[MaxChildNodeCount],
|
||||
new CallNode[MaxChildNodeCount],
|
||||
new CallNode[MaxChildNodeCount],
|
||||
new CallNode[MaxChildNodeCount]
|
||||
};
|
||||
private const int MaxChildNodeCount = 16; // 每个分支最多支持16个子节点
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的子节点数量。
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public int GetCount(ConnectionInvokeType type)
|
||||
{
|
||||
if (type == ConnectionInvokeType.Upstream) return UpstreamNodeCount;
|
||||
if (type == ConnectionInvokeType.IsSucceed) return IsSuccessorNodeCount;
|
||||
if (type == ConnectionInvokeType.IsFail) return IsFailNodeCount;
|
||||
if (type == ConnectionInvokeType.IsError) return IsErrorNodeCount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的子节点数量。
|
||||
/// </summary>
|
||||
public int UpstreamNodeCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的成功后继子节点数量。
|
||||
/// </summary>
|
||||
public int IsSuccessorNodeCount { get; private set; } = 0;
|
||||
/// <summary>
|
||||
/// 获取当前节点的失败后继子节点数量。
|
||||
/// </summary>
|
||||
public int IsFailNodeCount { get; private set; } = 0;
|
||||
/// <summary>
|
||||
/// 获取当前节点的错误后继子节点数量。
|
||||
/// </summary>
|
||||
public int IsErrorNodeCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个上游子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeUpstream(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.Upstream;
|
||||
ChildNodes[(int)connectionInvokeType][UpstreamNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个成功后继子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeSucceed(CallNode callNode)
|
||||
{
|
||||
ChildNodes[0][UpstreamNodeCount++] = callNode;
|
||||
|
||||
var connectionInvokeType = ConnectionInvokeType.IsSucceed;
|
||||
ChildNodes[(int)connectionInvokeType][IsSuccessorNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加一个失败后继子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeFail(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsFail;
|
||||
ChildNodes[(int)connectionInvokeType][IsFailNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个错误后继子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeError(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsError;
|
||||
ChildNodes[(int)connectionInvokeType][IsErrorNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 调用
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public async Task InvokeAsync(IFlowContext context, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (actionType == ActionType.Action)
|
||||
{
|
||||
action.Invoke(context);
|
||||
}
|
||||
else if (actionType == ActionType.Task)
|
||||
{
|
||||
await taskFunc.Invoke(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"生成了错误的CallNode。【{Guid}】");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ObjectPool<Stack<CallNode>> _stackPool = new ObjectPool<Stack<CallNode>>(() => new Stack<CallNode>());
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="token">流程运行</param>
|
||||
/// <returns></returns>
|
||||
public async Task<FlowResult> StartFlowAsync(IFlowContext context, CancellationToken token)
|
||||
{
|
||||
var stack = _stackPool.Allocate();
|
||||
stack.Push(this);
|
||||
while (true)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
throw new Exception($"流程执行被取消,未能获取到流程结果。");
|
||||
}
|
||||
|
||||
#region 执行相关
|
||||
// 从栈中弹出一个节点作为当前节点进行处理
|
||||
var currentNode = stack.Pop();
|
||||
context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态
|
||||
FlowResult flowResult = null;
|
||||
try
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsSucceed; // 默认执行成功
|
||||
await currentNode.InvokeAsync(context, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
flowResult = FlowResult.Fail(currentNode.Guid, context, ex.Message);
|
||||
context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode}]异常:" + ex);
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
context.ExceptionOfRuning = ex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 执行完成时更新栈
|
||||
// 首先将指定类别后继分支的所有节点逆序推入栈中
|
||||
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
|
||||
for (int index = nextNodes.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var node = nextNodes[index];
|
||||
context.SetPreviousNode(node.Guid, currentNode.Guid);
|
||||
stack.Push(node);
|
||||
}
|
||||
|
||||
// 然后将指上游分支的所有节点逆序推入栈中
|
||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream];
|
||||
for (int index = upstreamNodes.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var node = upstreamNodes[index];
|
||||
context.SetPreviousNode(node.Guid, currentNode.Guid);
|
||||
stack.Push(node);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 执行完成后检查
|
||||
|
||||
if (stack.Count == 0)
|
||||
{
|
||||
_stackPool.Free(stack);
|
||||
flowResult = context.GetFlowData(currentNode.Guid);
|
||||
return flowResult; // 说明流程到了终点
|
||||
}
|
||||
|
||||
if (context.RunState == RunState.Completion)
|
||||
{
|
||||
|
||||
_stackPool.Free(stack);
|
||||
context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
|
||||
flowResult = context.GetFlowData(currentNode.Guid);
|
||||
return flowResult; // 流程执行完成,返回结果
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
_stackPool.Free(stack);
|
||||
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
76
Library/FlowNode/Env/FlowCallTree.cs
Normal file
76
Library/FlowNode/Env/FlowCallTree.cs
Normal file
@@ -0,0 +1,76 @@
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程调用树,管理所有的调用节点
|
||||
/// </summary>
|
||||
public class FlowCallTree : IFlowCallTree
|
||||
{
|
||||
|
||||
private readonly SortedDictionary<string, CallNode> _callNodes = new SortedDictionary<string,CallNode>();
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<CallNode> StartNodes { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public List<CallNode> GlobalFlipflopNodes { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 索引器,允许通过字符串索引访问CallNode
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode this[string index]
|
||||
{
|
||||
get
|
||||
{
|
||||
_callNodes.TryGetValue(index, out CallNode callNode);
|
||||
return callNode;
|
||||
}
|
||||
set
|
||||
{
|
||||
// 设置指定索引的值
|
||||
_callNodes.Add(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个调用节点到流程调用树中
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="action"></param>
|
||||
public void AddCallNode(string nodeGuid, Action<IFlowContext> action)
|
||||
{
|
||||
var node = new CallNode(nodeGuid, action);
|
||||
_callNodes[nodeGuid] = node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个调用节点到流程调用树中,使用异步函数
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="func"></param>
|
||||
public void AddCallNode(string nodeGuid, Func<IFlowContext, Task> func)
|
||||
{
|
||||
var node = new CallNode(nodeGuid, func);
|
||||
_callNodes[nodeGuid] = node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定Key的CallNode,如果不存在则返回null
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode Get(string key)
|
||||
{
|
||||
return _callNodes.TryGetValue(key, out CallNode callNode) ? callNode : null;
|
||||
}
|
||||
}
|
||||
}
|
||||
35
Library/FlowNode/Env/IFlowCallTree.cs
Normal file
35
Library/FlowNode/Env/IFlowCallTree.cs
Normal file
@@ -0,0 +1,35 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 流程调用树接口,提供获取CallNode的方法。
|
||||
/// </summary>
|
||||
public interface IFlowCallTree
|
||||
{
|
||||
/// <summary>
|
||||
/// 起始节点
|
||||
/// </summary>
|
||||
List<CallNode> StartNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 全局触发器节点列表
|
||||
/// </summary>
|
||||
List<CallNode> GlobalFlipflopNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 初始化并启动流程调用树,异步执行。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task InitAndStartAsync(CancellationToken token);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定Key的CallNode,如果不存在则返回null。
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
CallNode Get(string key);
|
||||
}
|
||||
}
|
||||
190
Library/FlowNode/Env/LightweightFlowControl.cs
Normal file
190
Library/FlowNode/Env/LightweightFlowControl.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 轻量级流程控制器
|
||||
/// </summary>
|
||||
public class LightweightFlowControl : IFlowControl
|
||||
{
|
||||
private readonly IFlowCallTree flowCallTree;
|
||||
private readonly IFlowEnvironment flowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程上下文池,使用对象池模式来管理流程上下文的创建和回收。
|
||||
/// </summary>
|
||||
public static Serein.Library.Utils.ObjectPool<IFlowContext> FlowContextPool { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单例IOC容器,用于依赖注入和服务定位。
|
||||
/// </summary>
|
||||
public ISereinIOC IOC => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程控制器构造函数,接受流程调用树和流程环境作为参数。
|
||||
/// </summary>
|
||||
/// <param name="flowCallTree"></param>
|
||||
/// <param name="flowEnvironment"></param>
|
||||
public LightweightFlowControl(IFlowCallTree flowCallTree, IFlowEnvironment flowEnvironment)
|
||||
{
|
||||
this.flowCallTree = flowCallTree;
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
((LightweightFlowEnvironment)flowEnvironment).FlowControl = this;
|
||||
|
||||
FlowContextPool = new Utils.ObjectPool<IFlowContext>(() =>
|
||||
{
|
||||
return new FlowContext(flowEnvironment);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<object> InvokeAsync(string apiGuid, Dictionary<string, object> dict)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task<TResult> InvokeAsync<TResult>(string apiGuid, Dictionary<string, object> dict)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
//private readonly DefaultObjectPool<IDynamicContext> _stackPool = new DefaultObjectPool<IDynamicContext>(new DynamicContext(this));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
|
||||
{
|
||||
IFlowContext context = Serein.Library.LightweightFlowControl.FlowContextPool.Allocate();
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
FlowResult flowResult;
|
||||
#if DEBUG
|
||||
flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
||||
{
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
var flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
return flowResult;
|
||||
});
|
||||
#else
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
try
|
||||
{
|
||||
flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
}
|
||||
catch (global::System.Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Reset();
|
||||
FlowContextPool.Free(context);
|
||||
}
|
||||
#endif
|
||||
|
||||
cts?.Cancel();
|
||||
cts?.Dispose();
|
||||
if (flowResult.Value is TResult result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else if (flowResult is FlowResult && flowResult is TResult result2)
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task StartFlowAsync(string startNodeGuid)
|
||||
{
|
||||
IFlowContext context = Serein.Library.LightweightFlowControl.FlowContextPool.Allocate();
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
FlowResult flowResult;
|
||||
#if DEBUG
|
||||
flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
||||
{
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
var flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
return flowResult;
|
||||
});
|
||||
#else
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
try
|
||||
{
|
||||
flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
}
|
||||
catch (global::System.Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Reset();
|
||||
FlowContextPool.Free(context);
|
||||
}
|
||||
#endif
|
||||
|
||||
cts?.Cancel();
|
||||
cts?.Dispose();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> ExitFlowAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#region 无须实现
|
||||
/// <inheritdoc/>
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void UseExternalIOC(ISereinIOC ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC> setDefultMemberOnReset = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
146
Library/FlowNode/Env/LightweightFlowEnvironment.cs
Normal file
146
Library/FlowNode/Env/LightweightFlowEnvironment.cs
Normal file
@@ -0,0 +1,146 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程环境实现
|
||||
/// </summary>
|
||||
public class LightweightFlowEnvironment : IFlowEnvironment
|
||||
{
|
||||
/// <summary>
|
||||
/// 轻量级流程环境构造函数,接受一个流程环境事件接口。
|
||||
/// </summary>
|
||||
/// <param name="lightweightFlowEnvironmentEvent"></param>
|
||||
public LightweightFlowEnvironment(IFlowEnvironmentEvent lightweightFlowEnvironmentEvent)
|
||||
{
|
||||
Event = lightweightFlowEnvironmentEvent;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Debug)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISereinIOC IOC => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public IFlowEdit FlowEdit => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public IFlowControl FlowControl { get; set; }
|
||||
/// <inheritdoc/>
|
||||
public IFlowEnvironmentEvent Event { get; private set; }
|
||||
/// <inheritdoc/>
|
||||
public string EnvName => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public string ProjectFileLocation => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public bool _IsGlobalInterrupt => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public bool IsControlRemoteEnv => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public InfoClass InfoClass { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
/// <inheritdoc/>
|
||||
public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
/// <inheritdoc/>
|
||||
public IFlowEnvironment CurrentEnv => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public UIContextOperation UIContextOperation => throw new NotImplementedException();
|
||||
|
||||
/* public Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}*/
|
||||
/// <inheritdoc/>
|
||||
public void ExitRemoteEnv()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public SereinProjectData GetProjectInfoAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LoadLibrary(string dllPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool LoadNativeLibraryOfRuning(string file)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LoadProject(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task LoadProjetAsync(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void SaveProject()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void SetUIContextOperation(UIContextOperation uiContextOperation)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task StartRemoteServerAsync(int port = 7525)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void StopRemoteServer()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryUnloadLibrary(string assemblyFullName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
138
Library/FlowNode/Env/LightweightFlowEnvironmentEvent.cs
Normal file
138
Library/FlowNode/Env/LightweightFlowEnvironmentEvent.cs
Normal file
@@ -0,0 +1,138 @@
|
||||
using Serein.Library.Api;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 轻量级流程环境事件实现
|
||||
/// </summary>
|
||||
public class LightweightFlowEnvironmentEvent : IFlowEnvironmentEvent
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public event LoadDllHandler DllLoad;
|
||||
/// <inheritdoc/>
|
||||
public event ProjectLoadedHandler ProjectLoaded;
|
||||
/// <inheritdoc/>
|
||||
public event ProjectSavingHandler ProjectSaving;
|
||||
/// <inheritdoc/>
|
||||
public event NodeConnectChangeHandler NodeConnectChanged;
|
||||
/// <inheritdoc/>
|
||||
public event CanvasCreateHandler CanvasCreated;
|
||||
/// <inheritdoc/>
|
||||
public event CanvasRemoveHandler CanvasRemoved;
|
||||
/// <inheritdoc/>
|
||||
public event NodeCreateHandler NodeCreated;
|
||||
/// <inheritdoc/>
|
||||
public event NodeRemoveHandler NodeRemoved;
|
||||
/// <inheritdoc/>
|
||||
public event NodePlaceHandler NodePlace;
|
||||
/// <inheritdoc/>
|
||||
public event NodeTakeOutHandler NodeTakeOut;
|
||||
/// <inheritdoc/>
|
||||
public event StartNodeChangeHandler StartNodeChanged;
|
||||
/// <inheritdoc/>
|
||||
public event FlowRunCompleteHandler FlowRunComplete;
|
||||
/// <inheritdoc/>
|
||||
public event MonitorObjectChangeHandler MonitorObjectChanged;
|
||||
/// <inheritdoc/>
|
||||
public event NodeInterruptStateChangeHandler NodeInterruptStateChanged;
|
||||
/// <inheritdoc/>
|
||||
public event ExpInterruptTriggerHandler InterruptTriggered;
|
||||
/// <inheritdoc/>
|
||||
public event IOCMembersChangedHandler IOCMembersChanged;
|
||||
/// <inheritdoc/>
|
||||
public event NodeLocatedHandler NodeLocated;
|
||||
/// <inheritdoc/>
|
||||
public event EnvOutHandler EnvOutput;
|
||||
/// <inheritdoc/>
|
||||
public void OnDllLoad(LoadDllEventArgs eventArgs)
|
||||
{
|
||||
DllLoad?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
|
||||
{
|
||||
ProjectLoaded?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnProjectSaving(ProjectSavingEventArgs eventArgs)
|
||||
{
|
||||
ProjectSaving?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs)
|
||||
{
|
||||
NodeConnectChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnCanvasCreated(CanvasCreateEventArgs eventArgs)
|
||||
{
|
||||
CanvasCreated?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs)
|
||||
{
|
||||
CanvasRemoved?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeCreated(NodeCreateEventArgs eventArgs)
|
||||
{
|
||||
NodeCreated?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeRemoved(NodeRemoveEventArgs eventArgs)
|
||||
{
|
||||
NodeRemoved?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodePlace(NodePlaceEventArgs eventArgs)
|
||||
{
|
||||
NodePlace?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
|
||||
{
|
||||
NodeTakeOut?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs)
|
||||
{
|
||||
StartNodeChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnFlowRunComplete(FlowEventArgs eventArgs)
|
||||
{
|
||||
FlowRunComplete?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs)
|
||||
{
|
||||
MonitorObjectChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs)
|
||||
{
|
||||
NodeInterruptStateChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs)
|
||||
{
|
||||
InterruptTriggered?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs)
|
||||
{
|
||||
IOCMembersChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeLocated(NodeLocatedEventArgs eventArgs)
|
||||
{
|
||||
NodeLocated?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnEnvOutput(InfoType type, string value)
|
||||
{
|
||||
EnvOutput?.Invoke(type, value);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,5 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.FlowNode;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
|
||||
@@ -5,7 +5,7 @@ using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.FlowNode
|
||||
namespace Serein.Library
|
||||
{
|
||||
|
||||
|
||||
|
||||
@@ -1,824 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 流程调用树,管理所有的调用节点
|
||||
/// </summary>
|
||||
public class FlowCallTree : IFlowCallTree
|
||||
{
|
||||
|
||||
private readonly SortedDictionary<string, CallNode> _callNodes = new SortedDictionary<string,CallNode>();
|
||||
|
||||
/// <summary>
|
||||
/// 索引器,允许通过字符串索引访问CallNode
|
||||
/// </summary>
|
||||
/// <param name="index"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode this[string index]
|
||||
{
|
||||
get
|
||||
{
|
||||
_callNodes.TryGetValue(index, out CallNode callNode);
|
||||
return callNode;
|
||||
}
|
||||
set
|
||||
{
|
||||
// 设置指定索引的值
|
||||
_callNodes.Add(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个调用节点到流程调用树中
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="action"></param>
|
||||
public void AddCallNode(string nodeGuid, Action<IFlowContext> action)
|
||||
{
|
||||
var node = new CallNode(nodeGuid, action);
|
||||
_callNodes[nodeGuid] = node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个调用节点到流程调用树中,使用异步函数
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="func"></param>
|
||||
public void AddCallNode(string nodeGuid, Func<IFlowContext, Task> func)
|
||||
{
|
||||
var node = new CallNode(nodeGuid, func);
|
||||
_callNodes[nodeGuid] = node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定Key的CallNode,如果不存在则返回null
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode Get(string key)
|
||||
{
|
||||
return _callNodes.TryGetValue(key, out CallNode callNode) ? callNode : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 调用节点,代表一个流程中的调用点,可以是一个Action或一个异步函数。
|
||||
/// </summary>
|
||||
|
||||
public class CallNode
|
||||
{
|
||||
|
||||
private Func<IFlowContext, Task> taskFunc;
|
||||
private Action<IFlowContext> action;
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的调用节点,使用指定的节点Guid。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
public CallNode(string nodeGuid)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的调用节点,使用指定的节点Guid和Action。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="action"></param>
|
||||
public CallNode(string nodeGuid, Action<IFlowContext> action)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.action = action;
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的调用节点,使用指定的节点Guid和异步函数。
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="func"></param>
|
||||
public CallNode(string nodeGuid, Func<IFlowContext, Task> func)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.taskFunc = func;
|
||||
Init();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化调用节点,设置默认的子节点和后继节点字典。
|
||||
/// </summary>
|
||||
private void Init()
|
||||
{
|
||||
//PreviousNodes = new Dictionary<ConnectionInvokeType, List<CallNode>>();
|
||||
SuccessorNodes = new Dictionary<ConnectionInvokeType, List<CallNode>>();
|
||||
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
//PreviousNodes[ctType] = new List<CallNode>();
|
||||
SuccessorNodes[ctType] = new List<CallNode>();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private enum ActionType
|
||||
{
|
||||
Action,
|
||||
Task,
|
||||
}
|
||||
private ActionType actionType = ActionType.Action;
|
||||
|
||||
/// <summary>
|
||||
/// 设置调用节点的Action,表示该节点执行一个同步操作。
|
||||
/// </summary>
|
||||
/// <param name="action"></param>
|
||||
public void SetAction(Action<IFlowContext> action)
|
||||
{
|
||||
this.action = action;
|
||||
actionType = ActionType.Action;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置调用节点的异步函数,表示该节点执行一个异步操作。
|
||||
/// </summary>
|
||||
/// <param name="taskFunc"></param>
|
||||
public void SetAction(Func<IFlowContext, Task> taskFunc)
|
||||
{
|
||||
this.taskFunc = taskFunc;
|
||||
actionType = ActionType.Task;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 对应的节点
|
||||
/// </summary>
|
||||
public string Guid { get; }
|
||||
|
||||
#if false
|
||||
|
||||
/// <summary>
|
||||
/// 不同分支的父节点(流程调用)
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionInvokeType, List<CallNode>> PreviousNodes { get; private set; }
|
||||
|
||||
#endif
|
||||
/// <summary>
|
||||
/// 不同分支的子节点(流程调用)
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionInvokeType, List<CallNode>> SuccessorNodes { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子节点数组,分为四个分支:上游、成功、失败、错误,每个分支最多支持16个子节点。
|
||||
/// </summary>
|
||||
public CallNode[][] ChildNodes { get; private set; } = new CallNode[][]
|
||||
{
|
||||
new CallNode[MaxChildNodeCount],
|
||||
new CallNode[MaxChildNodeCount],
|
||||
new CallNode[MaxChildNodeCount],
|
||||
new CallNode[MaxChildNodeCount]
|
||||
};
|
||||
private const int MaxChildNodeCount = 16; // 每个分支最多支持16个子节点
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定类型的子节点数量。
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <returns></returns>
|
||||
public int GetCount(ConnectionInvokeType type)
|
||||
{
|
||||
if (type == ConnectionInvokeType.Upstream) return UpstreamNodeCount;
|
||||
if (type == ConnectionInvokeType.IsSucceed) return IsSuccessorNodeCount;
|
||||
if (type == ConnectionInvokeType.IsFail) return IsFailNodeCount;
|
||||
if (type == ConnectionInvokeType.IsError) return IsErrorNodeCount;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的子节点数量。
|
||||
/// </summary>
|
||||
public int UpstreamNodeCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的成功后继子节点数量。
|
||||
/// </summary>
|
||||
public int IsSuccessorNodeCount { get; private set; } = 0;
|
||||
/// <summary>
|
||||
/// 获取当前节点的失败后继子节点数量。
|
||||
/// </summary>
|
||||
public int IsFailNodeCount { get; private set; } = 0;
|
||||
/// <summary>
|
||||
/// 获取当前节点的错误后继子节点数量。
|
||||
/// </summary>
|
||||
public int IsErrorNodeCount { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个上游子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeUpstream(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.Upstream;
|
||||
ChildNodes[(int)connectionInvokeType][UpstreamNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个成功后继子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeSucceed(CallNode callNode)
|
||||
{
|
||||
ChildNodes[0][UpstreamNodeCount++] = callNode;
|
||||
|
||||
var connectionInvokeType = ConnectionInvokeType.IsSucceed;
|
||||
ChildNodes[(int)connectionInvokeType][IsSuccessorNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
/// <summary>
|
||||
/// 添加一个失败后继子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeFail(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsFail;
|
||||
ChildNodes[(int)connectionInvokeType][IsFailNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个错误后继子节点到当前节点。
|
||||
/// </summary>
|
||||
/// <param name="callNode"></param>
|
||||
/// <returns></returns>
|
||||
public CallNode AddChildNodeError(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsError;
|
||||
ChildNodes[(int)connectionInvokeType][IsErrorNodeCount++] = callNode;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 调用
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public async Task InvokeAsync(IFlowContext context, CancellationToken token)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (actionType == ActionType.Action)
|
||||
{
|
||||
action.Invoke(context);
|
||||
}
|
||||
else if (actionType == ActionType.Task)
|
||||
{
|
||||
await taskFunc.Invoke(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"生成了错误的CallNode。【{Guid}】");
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly ObjectPool<Stack<CallNode>> _stackPool = new ObjectPool<Stack<CallNode>>(() => new Stack<CallNode>());
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
/// <param name="context"></param>
|
||||
/// <param name="token">流程运行</param>
|
||||
/// <returns></returns>
|
||||
public async Task<FlowResult> StartFlowAsync(IFlowContext context, CancellationToken token)
|
||||
{
|
||||
var stack = _stackPool.Allocate();
|
||||
stack.Push(this);
|
||||
while (true)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
throw new Exception($"流程执行被取消,未能获取到流程结果。");
|
||||
}
|
||||
|
||||
#region 执行相关
|
||||
// 从栈中弹出一个节点作为当前节点进行处理
|
||||
var currentNode = stack.Pop();
|
||||
context.NextOrientation = ConnectionInvokeType.None; // 重置上下文状态
|
||||
FlowResult flowResult = null;
|
||||
try
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsSucceed; // 默认执行成功
|
||||
await currentNode.InvokeAsync(context, token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
flowResult = FlowResult.Fail(currentNode.Guid, context, ex.Message);
|
||||
context.Env.WriteLine(InfoType.ERROR, $"节点[{currentNode}]异常:" + ex);
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
context.ExceptionOfRuning = ex;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 执行完成时更新栈
|
||||
// 首先将指定类别后继分支的所有节点逆序推入栈中
|
||||
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
|
||||
for (int index = nextNodes.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var node = nextNodes[index];
|
||||
context.SetPreviousNode(node.Guid, currentNode.Guid);
|
||||
stack.Push(node);
|
||||
}
|
||||
|
||||
// 然后将指上游分支的所有节点逆序推入栈中
|
||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream];
|
||||
for (int index = upstreamNodes.Count - 1; index >= 0; index--)
|
||||
{
|
||||
var node = upstreamNodes[index];
|
||||
context.SetPreviousNode(node.Guid, currentNode.Guid);
|
||||
stack.Push(node);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 执行完成后检查
|
||||
|
||||
if (stack.Count == 0)
|
||||
{
|
||||
_stackPool.Free(stack);
|
||||
flowResult = context.GetFlowData(currentNode.Guid);
|
||||
return flowResult; // 说明流程到了终点
|
||||
}
|
||||
|
||||
if (context.RunState == RunState.Completion)
|
||||
{
|
||||
|
||||
_stackPool.Free(stack);
|
||||
context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
|
||||
flowResult = context.GetFlowData(currentNode.Guid);
|
||||
return flowResult; // 流程执行完成,返回结果
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
_stackPool.Free(stack);
|
||||
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 流程调用树接口,提供获取CallNode的方法。
|
||||
/// </summary>
|
||||
public interface IFlowCallTree
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取指定Key的CallNode,如果不存在则返回null。
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
CallNode Get(string key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程控制器
|
||||
/// </summary>
|
||||
public class LightweightFlowControl : IFlowControl
|
||||
{
|
||||
private readonly IFlowCallTree flowCallTree;
|
||||
private readonly IFlowEnvironment flowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程上下文池,使用对象池模式来管理流程上下文的创建和回收。
|
||||
/// </summary>
|
||||
public static Serein.Library.Utils.ObjectPool<IFlowContext> FlowContextPool { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 单例IOC容器,用于依赖注入和服务定位。
|
||||
/// </summary>
|
||||
public ISereinIOC IOC => throw new NotImplementedException();
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程控制器构造函数,接受流程调用树和流程环境作为参数。
|
||||
/// </summary>
|
||||
/// <param name="flowCallTree"></param>
|
||||
/// <param name="flowEnvironment"></param>
|
||||
public LightweightFlowControl(IFlowCallTree flowCallTree, IFlowEnvironment flowEnvironment)
|
||||
{
|
||||
this.flowCallTree = flowCallTree;
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
FlowContextPool = new Utils.ObjectPool<IFlowContext>(() =>
|
||||
{
|
||||
return new FlowContext(flowEnvironment);
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<object> InvokeAsync(string apiGuid, Dictionary<string, object> dict)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task<TResult> InvokeAsync<TResult>(string apiGuid, Dictionary<string, object> dict)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
//private readonly DefaultObjectPool<IDynamicContext> _stackPool = new DefaultObjectPool<IDynamicContext>(new DynamicContext(this));
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
|
||||
{
|
||||
IFlowContext context = Serein.Library.LightweightFlowControl.FlowContextPool.Allocate();
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
FlowResult flowResult;
|
||||
#if DEBUG
|
||||
flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
||||
{
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
var flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
return flowResult;
|
||||
});
|
||||
#else
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
try
|
||||
{
|
||||
flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
}
|
||||
catch (global::System.Exception)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
context.Reset();
|
||||
FlowContextPool.Free(context);
|
||||
}
|
||||
#endif
|
||||
|
||||
cts?.Cancel();
|
||||
cts?.Dispose();
|
||||
if (flowResult.Value is TResult result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else if (flowResult is FlowResult && flowResult is TResult result2)
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。");
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> ExitFlowAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
#region 无须实现
|
||||
/// <inheritdoc/>
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void UseExternalIOC(ISereinIOC ioc)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC> setDefultMemberOnReset = null)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程环境事件实现
|
||||
/// </summary>
|
||||
public class LightweightFlowEnvironmentEvent : IFlowEnvironmentEvent
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public event LoadDllHandler DllLoad;
|
||||
/// <inheritdoc/>
|
||||
public event ProjectLoadedHandler ProjectLoaded;
|
||||
/// <inheritdoc/>
|
||||
public event ProjectSavingHandler ProjectSaving;
|
||||
/// <inheritdoc/>
|
||||
public event NodeConnectChangeHandler NodeConnectChanged;
|
||||
/// <inheritdoc/>
|
||||
public event CanvasCreateHandler CanvasCreated;
|
||||
/// <inheritdoc/>
|
||||
public event CanvasRemoveHandler CanvasRemoved;
|
||||
/// <inheritdoc/>
|
||||
public event NodeCreateHandler NodeCreated;
|
||||
/// <inheritdoc/>
|
||||
public event NodeRemoveHandler NodeRemoved;
|
||||
/// <inheritdoc/>
|
||||
public event NodePlaceHandler NodePlace;
|
||||
/// <inheritdoc/>
|
||||
public event NodeTakeOutHandler NodeTakeOut;
|
||||
/// <inheritdoc/>
|
||||
public event StartNodeChangeHandler StartNodeChanged;
|
||||
/// <inheritdoc/>
|
||||
public event FlowRunCompleteHandler FlowRunComplete;
|
||||
/// <inheritdoc/>
|
||||
public event MonitorObjectChangeHandler MonitorObjectChanged;
|
||||
/// <inheritdoc/>
|
||||
public event NodeInterruptStateChangeHandler NodeInterruptStateChanged;
|
||||
/// <inheritdoc/>
|
||||
public event ExpInterruptTriggerHandler InterruptTriggered;
|
||||
/// <inheritdoc/>
|
||||
public event IOCMembersChangedHandler IOCMembersChanged;
|
||||
/// <inheritdoc/>
|
||||
public event NodeLocatedHandler NodeLocated;
|
||||
/// <inheritdoc/>
|
||||
public event EnvOutHandler EnvOutput;
|
||||
/// <inheritdoc/>
|
||||
public void OnDllLoad(LoadDllEventArgs eventArgs)
|
||||
{
|
||||
DllLoad?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
|
||||
{
|
||||
ProjectLoaded?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnProjectSaving(ProjectSavingEventArgs eventArgs)
|
||||
{
|
||||
ProjectSaving?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs)
|
||||
{
|
||||
NodeConnectChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnCanvasCreated(CanvasCreateEventArgs eventArgs)
|
||||
{
|
||||
CanvasCreated?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs)
|
||||
{
|
||||
CanvasRemoved?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeCreated(NodeCreateEventArgs eventArgs)
|
||||
{
|
||||
NodeCreated?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeRemoved(NodeRemoveEventArgs eventArgs)
|
||||
{
|
||||
NodeRemoved?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodePlace(NodePlaceEventArgs eventArgs)
|
||||
{
|
||||
NodePlace?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
|
||||
{
|
||||
NodeTakeOut?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs)
|
||||
{
|
||||
StartNodeChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnFlowRunComplete(FlowEventArgs eventArgs)
|
||||
{
|
||||
FlowRunComplete?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs)
|
||||
{
|
||||
MonitorObjectChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs)
|
||||
{
|
||||
NodeInterruptStateChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs)
|
||||
{
|
||||
InterruptTriggered?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs)
|
||||
{
|
||||
IOCMembersChanged?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnNodeLocated(NodeLocatedEventArgs eventArgs)
|
||||
{
|
||||
NodeLocated?.Invoke(eventArgs);
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void OnEnvOutput(InfoType type, string value)
|
||||
{
|
||||
EnvOutput?.Invoke(type, value);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程环境实现
|
||||
/// </summary>
|
||||
public class LightweightFlowEnvironment : IFlowEnvironment
|
||||
{
|
||||
/// <summary>
|
||||
/// 轻量级流程环境构造函数,接受一个流程环境事件接口。
|
||||
/// </summary>
|
||||
/// <param name="lightweightFlowEnvironmentEvent"></param>
|
||||
public LightweightFlowEnvironment(IFlowEnvironmentEvent lightweightFlowEnvironmentEvent)
|
||||
{
|
||||
this.Event = lightweightFlowEnvironmentEvent;
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Debug)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public ISereinIOC IOC => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public IFlowEdit FlowEdit => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public IFlowControl FlowControl => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public IFlowEnvironmentEvent Event { get; private set; }
|
||||
/// <inheritdoc/>
|
||||
public string EnvName => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public string ProjectFileLocation => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public bool _IsGlobalInterrupt => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public bool IsControlRemoteEnv => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public InfoClass InfoClass { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
/// <inheritdoc/>
|
||||
public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
|
||||
/// <inheritdoc/>
|
||||
public IFlowEnvironment CurrentEnv => throw new NotImplementedException();
|
||||
/// <inheritdoc/>
|
||||
public UIContextOperation UIContextOperation => throw new NotImplementedException();
|
||||
|
||||
/* public Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}*/
|
||||
/// <inheritdoc/>
|
||||
public void ExitRemoteEnv()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public SereinProjectData GetProjectInfoAsync()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LoadLibrary(string dllPath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool LoadNativeLibraryOfRuning(string file)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void LoadProject(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task LoadProjetAsync(string filePath)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void SaveProject()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void SetUIContextOperation(UIContextOperation uiContextOperation)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public Task StartRemoteServerAsync(int port = 7525)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public void StopRemoteServer()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
/// <inheritdoc/>
|
||||
public bool TryUnloadLibrary(string assemblyFullName)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -59,6 +59,7 @@
|
||||
<Compile Remove="Enums\FlipflopStateType.cs" />
|
||||
<Compile Remove="Extension\FlowModelExtension.cs" />
|
||||
<Compile Remove="FlowNode\Attribute.cs" />
|
||||
<Compile Remove="FlowNode\Env\FlowCallTree.cs" />
|
||||
<Compile Remove="FlowNode\FlipflopContext.cs" />
|
||||
<Compile Remove="FlowNode\NodeModelBaseData.cs" />
|
||||
<Compile Remove="FlowNode\NodeModelBaseFunc.cs" />
|
||||
|
||||
@@ -274,6 +274,67 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public async Task StartFlowAsync(string startNodeGuid)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
var checkpoints = new Dictionary<string, TimeSpan>();
|
||||
var flowWorkManagement = GetFWM();
|
||||
if (!flowModelService.TryGetNodeModel(startNodeGuid, out IFlowNode? nodeModel))
|
||||
{
|
||||
throw new Exception($"节点不存在【{startNodeGuid}】");
|
||||
}
|
||||
if(nodeModel is SingleFlipflopNode)
|
||||
{
|
||||
throw new Exception("不能从[Flipflop]节点开始");
|
||||
}
|
||||
|
||||
|
||||
var flowContextPool = flowWorkManagement.WorkOptions.FlowContextPool;
|
||||
var context = flowContextPool.Allocate();
|
||||
checkpoints["准备调用环境"] = sw.Elapsed;
|
||||
var flowResult = await nodeModel.StartFlowAsync(context, flowWorkManagement.WorkOptions.CancellationTokenSource.Token); // 开始运行时从选定节点开始运行
|
||||
checkpoints["调用节点流程"] = sw.Elapsed;
|
||||
|
||||
var last = TimeSpan.Zero;
|
||||
foreach (var kv in checkpoints)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"{kv.Key} 耗时: {(kv.Value - last).TotalMilliseconds} ms");
|
||||
last = kv.Value;
|
||||
}
|
||||
//await BenchmarkHelpers.BenchmarkAsync(flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel));
|
||||
if (context.IsRecordInvokeInfo)
|
||||
{
|
||||
var invokeInfos = context.GetAllInvokeInfos();
|
||||
_ = Task.Delay(100).ContinueWith(async (task) =>
|
||||
{
|
||||
await task;
|
||||
if (invokeInfos.Count < 255)
|
||||
{
|
||||
foreach (var info in invokeInfos)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, info.ToString());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
double total = 0;
|
||||
for (int i = 0; i < invokeInfos.Count; i++)
|
||||
{
|
||||
total += invokeInfos[i].TS.TotalSeconds;
|
||||
}
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"运行次数:{invokeInfos.Count}");
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"平均耗时:{total / invokeInfos.Count}");
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"总耗时:{total}");
|
||||
}
|
||||
});
|
||||
}
|
||||
context.Reset();
|
||||
flowContextPool.Free(context);
|
||||
ReturnFWM(flowWorkManagement); // 释放流程任务管理器
|
||||
}
|
||||
|
||||
|
||||
/// <inheritdoc/>
|
||||
public Task<bool> ExitFlowAsync()
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using Serein.Extend.NewtonsoftJson;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.FlowNode;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
using Serein.Library;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Model.Infos;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using Serein.Script;
|
||||
using Serein.Script.Node;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.NodeFlow.Services
|
||||
@@ -45,6 +42,7 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
|
||||
var flowNodes = flowModelService.GetAllNodeModel().ToArray();
|
||||
var flowCanvass = flowModelService.GetAllCanvasModel().ToArray();
|
||||
// 收集程序集信息
|
||||
foreach (var node in flowNodes)
|
||||
{
|
||||
@@ -53,10 +51,8 @@ namespace Serein.NodeFlow.Services
|
||||
{
|
||||
assemblyFlowClasss.Add(instanceType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
var scriptNodes = flowModelService.GetAllNodeModel().Where(n => n.ControlType == NodeControlType.Script).OfType<SingleScriptNode>().ToArray();
|
||||
GenerateScript_InitSereinScriptMethodInfos(scriptNodes); // 初始化脚本方法
|
||||
|
||||
@@ -73,9 +69,17 @@ namespace Serein.NodeFlow.Services
|
||||
string flowApiInterfaceName = $"IFlowApiInvoke"; // 类名
|
||||
stringBuilder.AppendCode(0, $"public class {flowTemplateClassName} : {flowApiInterfaceName}, global::{typeof(IFlowCallTree).FullName}");
|
||||
stringBuilder.AppendCode(0, $"{{");
|
||||
|
||||
// 生成 IFlowCallTree 接口
|
||||
var listNodes = $"global::System.Collections.Generic.List<{typeof(CallNode).FullName}>";
|
||||
stringBuilder.AppendCode(1, $"public {listNodes} {nameof(IFlowCallTree.StartNodes)} {{ get; }} = new {listNodes}();");
|
||||
stringBuilder.AppendCode(1, $"public {listNodes} {nameof(IFlowCallTree.GlobalFlipflopNodes)} {{get; }} = new {listNodes}();");
|
||||
|
||||
|
||||
GenerateCtor(stringBuilder, flowTemplateClassName, assemblyFlowClasss); // 生成构造方法
|
||||
GenerateInitMethod(stringBuilder); // 生成初始化方法
|
||||
GenerateCallTree(stringBuilder, flowNodes); // 生成调用树
|
||||
GenerateCallTree(stringBuilder, flowNodes, flowCanvass); // 生成调用树
|
||||
Generate_InitAndStart(stringBuilder); // 生成 InitAndStartAsync
|
||||
GenerateNodeIndexLookup(stringBuilder, flowTemplateClassName, flowNodes); // 初始化节点缓存
|
||||
|
||||
// 生成每个节点的方法
|
||||
@@ -85,7 +89,7 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
|
||||
// 生成实现流程接口的实现方法
|
||||
var flowApiInfos = flowApiMethodInfos.Values.ToArray();
|
||||
var flowApiInfos = _flowApiMethodInfos.Values.ToArray();
|
||||
foreach (var info in flowApiInfos)
|
||||
{
|
||||
stringBuilder.AppendCode(2, info.ToObjPoolSignature());
|
||||
@@ -98,7 +102,7 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
|
||||
// 载入脚本节点转换的C#代码(载入类)
|
||||
var scriptInfos = scriptMethodInfos.Values.ToArray();
|
||||
var scriptInfos = _scriptMethodInfos.Values.ToArray();
|
||||
foreach (var info in scriptInfos)
|
||||
{
|
||||
stringBuilder.AppendCode(2, info.CsharpCode);
|
||||
@@ -168,10 +172,11 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
if (flowNode.ControlType == NodeControlType.Action && flowNode is SingleActionNode actionNode)
|
||||
{
|
||||
CreateMethodCore_Action(sb_main, actionNode, flowContextTypeName, flowContext);
|
||||
CreateMethodCore_ActionOrFliplop(sb_main, actionNode, flowContextTypeName, flowContext);
|
||||
}
|
||||
else if (flowNode.ControlType == NodeControlType.Flipflop)
|
||||
else if (flowNode.ControlType == NodeControlType.Flipflop && flowNode is SingleFlipflopNode flipflopNode)
|
||||
{
|
||||
CreateMethodCore_ActionOrFliplop(sb_main, flipflopNode, flowContextTypeName, flowContext);
|
||||
}
|
||||
else if (flowNode.ControlType == NodeControlType.Script && flowNode is SingleScriptNode singleScriptNode)
|
||||
{
|
||||
@@ -196,7 +201,6 @@ namespace Serein.NodeFlow.Services
|
||||
throw new Exception("无法为该节点生成调用逻辑");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成初始化方法(用于执行构造函数中无法完成的操作)
|
||||
/// </summary>
|
||||
@@ -215,16 +219,17 @@ namespace Serein.NodeFlow.Services
|
||||
/// <param name="sb"></param>
|
||||
/// <param name="flowNodes"></param>
|
||||
/// <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
private void GenerateCallTree(StringBuilder sb, IFlowNode[] flowNodes)
|
||||
private void GenerateCallTree(StringBuilder sb, IFlowNode[] flowNodes, FlowCanvasDetails[] flowCanvass)
|
||||
{
|
||||
// Get("0fa6985b-4b63-4499-80b2-76401669292d").AddChildNodeSucceed(Get("acdbe7ea-eb27-4a3e-9cc9-c48f642ee4f5"));
|
||||
sb.AppendCode(2, $"private void {nameof(GenerateCallTree)}()");
|
||||
sb.AppendCode(2, $"{{");
|
||||
|
||||
#region 设置节点回调
|
||||
foreach (var node in flowNodes)
|
||||
{
|
||||
var nodeMethod = node.ToNodeMethodName(); // 节点对应的方法名称
|
||||
if (node.ControlType == NodeControlType.Action
|
||||
if (node.ControlType == NodeControlType.Action
|
||||
|| node.ControlType == NodeControlType.Flipflop
|
||||
|| node.ControlType == NodeControlType.FlowCall
|
||||
|| node.ControlType == NodeControlType.Script
|
||||
)
|
||||
@@ -241,6 +246,8 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region 设置调用顺序
|
||||
var cts = NodeStaticConfig.ConnectionTypes;
|
||||
foreach (var node in flowNodes)
|
||||
{
|
||||
@@ -287,9 +294,29 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 实现接口
|
||||
var startNodeGuids = flowCanvass.Where(canvas => canvas.StartNode is not null).Select(canvas => canvas.StartNode!.Guid).ToList();
|
||||
foreach (var startNodeGuid in startNodeGuids)
|
||||
{
|
||||
sb.AppendCode(3, $"{nameof(IFlowCallTree.StartNodes)}.Add(Get(\"{startNodeGuid}\")); // 添加起始节点");
|
||||
}
|
||||
|
||||
var flipflopNodeGuids = flowNodes.Where(node => node.ControlType == NodeControlType.Flipflop)
|
||||
.OfType<SingleFlipflopNode>()
|
||||
.Where(node => node.IsRoot())
|
||||
.ToList();
|
||||
foreach (var flipflopNodeGuid in flipflopNodeGuids)
|
||||
{
|
||||
sb.AppendCode(3, $"{nameof(IFlowCallTree.GlobalFlipflopNodes)}.Add(Get(\"{flipflopNodeGuid.Guid}\")); // 添加全局触发器节点");
|
||||
}
|
||||
#endregion
|
||||
sb.AppendCode(2, $"}}");
|
||||
sb.AppendLine();
|
||||
|
||||
|
||||
|
||||
/*string? dynamicContextTypeName = typeof(IDynamicContext).FullName;
|
||||
string? flowContext = nameof(flowContext);
|
||||
var callTreeType = typeof(IFlowCallTree);
|
||||
@@ -400,6 +427,43 @@ namespace Serein.NodeFlow.Services
|
||||
sb.AppendCode(2, $"}}");
|
||||
}
|
||||
|
||||
private void Generate_InitAndStart(StringBuilder sb)
|
||||
{
|
||||
string value =
|
||||
"""
|
||||
/// <summary>
|
||||
/// 初始化并启动流程控制器,遍历所有的起始节点并启动对应的流程,同时处理全局触发器节点。
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async global::System.Threading.Tasks.Task InitAndStartAsync(global::System.Threading.CancellationToken token)
|
||||
{
|
||||
var startNodes = StartNodes.ToArray();
|
||||
foreach (var startNode in startNodes)
|
||||
{
|
||||
await flowEnvironment.FlowControl.StartFlowAsync(startNode.Guid);
|
||||
}
|
||||
var globalFlipflopNodes = GlobalFlipflopNodes.ToArray();
|
||||
var tasks = globalFlipflopNodes.Select(async node =>
|
||||
{
|
||||
while (!token.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
await flowEnvironment.FlowControl.StartFlowAsync(node.Guid);
|
||||
}
|
||||
catch (global::Serein.Library.FlipflopException ex)
|
||||
{
|
||||
if (ex.Type == global::Serein.Library.FlipflopException.CancelClass.CancelFlow)
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
""";
|
||||
sb.AppendLine(value);
|
||||
}
|
||||
|
||||
#region 节点方法生成
|
||||
|
||||
/// <summary>
|
||||
@@ -410,7 +474,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// <param name="flowContextTypeName"></param>
|
||||
/// <param name="flowContext"></param>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void CreateMethodCore_Action(StringBuilder sb_main, SingleActionNode actionNode, string? flowContextTypeName, string flowContext)
|
||||
private void CreateMethodCore_ActionOrFliplop(StringBuilder sb_main, IFlowNode actionNode, string? flowContextTypeName, string flowContext)
|
||||
{
|
||||
if (!flowLibraryService.TryGetMethodInfo(actionNode.MethodDetails.AssemblyName,
|
||||
actionNode.MethodDetails.MethodName,
|
||||
@@ -476,10 +540,18 @@ namespace Serein.NodeFlow.Services
|
||||
#region 非显式设置的参数以正常方式获取
|
||||
if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||
{
|
||||
var previousNode = $"previousNode{index}";
|
||||
|
||||
var valueType = pd.IsParams ? $"global::{pd.DataType.GetFriendlyName()}" : $"global::{paramtTypeFullName}";
|
||||
sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{actionNode.Guid}\");"); // 获取运行时上一节点Guid
|
||||
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IFlowContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
|
||||
if (typeof(IFlowContext).IsAssignableFrom(pd.DataType))
|
||||
{
|
||||
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {flowContext}; // 使用流程上下文");
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousNode = $"previousNode{index}";
|
||||
sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{actionNode.Guid}\");"); // 获取运行时上一节点Guid
|
||||
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IFlowContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
|
||||
}
|
||||
}
|
||||
else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
|
||||
{
|
||||
@@ -604,14 +676,14 @@ namespace Serein.NodeFlow.Services
|
||||
/// <exception cref="Exception"></exception>
|
||||
private void CreateMethodCore_FlowCall(StringBuilder sb_main, SingleFlowCallNode flowCallNode, string? flowContextTypeName, string flowContext)
|
||||
{
|
||||
if (!flowApiMethodInfos.TryGetValue(flowCallNode, out var flowApiMethodInfo))
|
||||
if (!_flowApiMethodInfos.TryGetValue(flowCallNode, out var flowApiMethodInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (flowCallNode.TargetNode is SingleScriptNode singleScriptNode)
|
||||
{
|
||||
if (!scriptMethodInfos.TryGetValue(singleScriptNode, out var scriptMethodInfo))
|
||||
if (!_scriptMethodInfos.TryGetValue(singleScriptNode, out var scriptMethodInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -796,7 +868,7 @@ namespace Serein.NodeFlow.Services
|
||||
{
|
||||
|
||||
|
||||
if (!scriptMethodInfos.TryGetValue(singleScriptNode, out var scriptMethodInfo))
|
||||
if (!_scriptMethodInfos.TryGetValue(singleScriptNode, out var scriptMethodInfo))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -854,10 +926,22 @@ namespace Serein.NodeFlow.Services
|
||||
#region 非显式设置的参数以正常方式获取
|
||||
if (pd.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||
{
|
||||
var previousNode = $"previousNode{index}";
|
||||
var valueType = pd.IsParams ? $"global::{pd.DataType.GetFriendlyName()}" : $"global::{paramtTypeFullName}";
|
||||
sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{singleScriptNode.Guid}\");"); // 获取运行时上一节点Guid
|
||||
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IFlowContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
|
||||
if (typeof(IFlowContext).IsAssignableFrom(pd.DataType))
|
||||
{
|
||||
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {flowContext}; // 使用流程上下文");
|
||||
}
|
||||
else
|
||||
{
|
||||
var previousNode = $"previousNode{index}";
|
||||
sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{singleScriptNode.Guid}\");"); // 获取运行时上一节点Guid
|
||||
sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IFlowContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
|
||||
}
|
||||
|
||||
// var previousNode = $"previousNode{index}";
|
||||
// var valueType = pd.IsParams ? $"global::{pd.DataType.GetFriendlyName()}" : $"global::{paramtTypeFullName}";
|
||||
// sb_invoke_login.AppendCode(3, $"global::System.String {previousNode} = {flowContext}.GetPreviousNode(\"{singleScriptNode.Guid}\");"); // 获取运行时上一节点Guid
|
||||
// sb_invoke_login.AppendCode(3, $"{valueType} value{index} = {previousNode} == null ? default : ({valueType}){flowContext}.{nameof(IFlowContext.GetFlowData)}({previousNode}).Value; // 获取运行时上一节点的数据");
|
||||
}
|
||||
else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
|
||||
{
|
||||
@@ -973,56 +1057,24 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
#endregion
|
||||
|
||||
#region 全局触发器与分支触发器生成
|
||||
|
||||
/*private Dictionary<SingleFlipflopNode, SereinFlipflopMethodInfo> _flipflopNodeInfos = [];
|
||||
|
||||
private class SereinFlipflopMethodInfo(bool isGlobal)
|
||||
{
|
||||
public bool IsGlobal { get; } = isGlobal;
|
||||
}
|
||||
private void GenerateFlipflop_InitSereinFlipflopMethodInfos(SingleFlipflopNode[])
|
||||
{
|
||||
}*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region 全局节点的代码生成
|
||||
|
||||
private Dictionary<SingleGlobalDataNode, SereinGlobalDataInfo> globalDataInfos = [];
|
||||
private Dictionary<SingleGlobalDataNode, SereinGlobalDataInfo> _globalDataInfos = [];
|
||||
private const string FlowGlobalData = nameof(FlowGlobalData);
|
||||
|
||||
private void GenerateGlobalData_InitSereinGlobalDataInfos(SingleGlobalDataNode[] globalDataNodes)
|
||||
{
|
||||
foreach(var node in globalDataNodes)
|
||||
{
|
||||
var keyName = node.KeyName;
|
||||
var dataNode = node.DataNode;
|
||||
if(dataNode is null)
|
||||
{
|
||||
throw new Exception($"全局数据节点[{node}]没有指定数据来源节点");
|
||||
}
|
||||
var type = dataNode.MethodDetails.ReturnType;
|
||||
if (type is null || type == typeof(void))
|
||||
{
|
||||
throw new Exception($"全局数据节点[{node}]无返回值");
|
||||
}
|
||||
globalDataInfos[node] = new SereinGlobalDataInfo
|
||||
{
|
||||
Node = node,
|
||||
DataSourceNode = dataNode,
|
||||
KeyName = keyName,
|
||||
DataType = type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成数据实体类
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
private void GenerateGlobalData_ToClass(StringBuilder sb)
|
||||
{
|
||||
var infos = globalDataInfos.Values.ToArray();
|
||||
sb.AppendCode(1, $"public sealed class {FlowGlobalData}");
|
||||
sb.AppendCode(1, $"{{");
|
||||
foreach (var info in infos)
|
||||
{
|
||||
var xmlDescription = $"{$"全局数据,来源于[{info.Node.MethodDetails?.MethodName}]{info.Node.Guid}".ToXmlComments(2)}";
|
||||
sb.AppendCode(2, xmlDescription);
|
||||
sb.AppendCode(2, $"public global::{info.DataType.FullName} {info.KeyName} {{ get; set; }};");
|
||||
}
|
||||
sb.AppendLine();
|
||||
sb.AppendCode(1, $"}}");
|
||||
}
|
||||
|
||||
|
||||
private class SereinGlobalDataInfo
|
||||
{
|
||||
/// <summary>
|
||||
@@ -1046,13 +1098,58 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
|
||||
|
||||
private void GenerateGlobalData_InitSereinGlobalDataInfos(SingleGlobalDataNode[] globalDataNodes)
|
||||
{
|
||||
foreach(var node in globalDataNodes)
|
||||
{
|
||||
var keyName = node.KeyName;
|
||||
var dataNode = node.DataNode;
|
||||
if(dataNode is null)
|
||||
{
|
||||
throw new Exception($"全局数据节点[{node}]没有指定数据来源节点");
|
||||
}
|
||||
var type = dataNode.MethodDetails.ReturnType;
|
||||
if (type is null || type == typeof(void))
|
||||
{
|
||||
throw new Exception($"全局数据节点[{node}]无返回值");
|
||||
}
|
||||
_globalDataInfos[node] = new SereinGlobalDataInfo
|
||||
{
|
||||
Node = node,
|
||||
DataSourceNode = dataNode,
|
||||
KeyName = keyName,
|
||||
DataType = type,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 生成数据实体类
|
||||
/// </summary>
|
||||
/// <param name="sb"></param>
|
||||
private void GenerateGlobalData_ToClass(StringBuilder sb)
|
||||
{
|
||||
var infos = _globalDataInfos.Values.ToArray();
|
||||
sb.AppendCode(1, $"public sealed class {FlowGlobalData}");
|
||||
sb.AppendCode(1, $"{{");
|
||||
foreach (var info in infos)
|
||||
{
|
||||
var xmlDescription = $"{$"全局数据,来源于[{info.Node.MethodDetails?.MethodName}]{info.Node.Guid}".ToXmlComments(2)}";
|
||||
sb.AppendCode(2, xmlDescription);
|
||||
sb.AppendCode(2, $"public global::{info.DataType.FullName} {info.KeyName} {{ get; set; }};");
|
||||
}
|
||||
sb.AppendLine();
|
||||
sb.AppendCode(1, $"}}");
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 脚本节点的代码生成
|
||||
private Dictionary<SingleScriptNode, SereinScriptMethodInfo> scriptMethodInfos = [];
|
||||
private Dictionary<SingleScriptNode, SereinScriptMethodInfo> _scriptMethodInfos = [];
|
||||
private void GenerateScript_InitSereinScriptMethodInfos(SingleScriptNode[] flowCallNodes)
|
||||
{
|
||||
scriptMethodInfos.Clear();
|
||||
_scriptMethodInfos.Clear();
|
||||
bool isError = false;
|
||||
foreach(var node in flowCallNodes)
|
||||
{
|
||||
@@ -1065,7 +1162,7 @@ namespace Serein.NodeFlow.Services
|
||||
}
|
||||
else
|
||||
{
|
||||
scriptMethodInfos[node] = info;
|
||||
_scriptMethodInfos[node] = info;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1082,7 +1179,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// 流程接口节点与对应的流程方法信息
|
||||
/// </summary>
|
||||
|
||||
private Dictionary<SingleFlowCallNode, FlowApiMethodInfo> flowApiMethodInfos = [];
|
||||
private Dictionary<SingleFlowCallNode, FlowApiMethodInfo> _flowApiMethodInfos = [];
|
||||
|
||||
/// <summary>
|
||||
/// 生成流程接口方法信息
|
||||
@@ -1090,7 +1187,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// <param name="flowCallNodes"></param>
|
||||
private void GenerateFlowApi_InitFlowApiMethodInfos(SingleFlowCallNode[] flowCallNodes)
|
||||
{
|
||||
flowApiMethodInfos.Clear();
|
||||
_flowApiMethodInfos.Clear();
|
||||
flowCallNodes = flowCallNodes.Where(node => !string.IsNullOrWhiteSpace(node.TargetNodeGuid)
|
||||
&& !flowModelService.ContainsCanvasModel(node.TargetNodeGuid))
|
||||
.ToArray(); // 筛选流程接口节点,只生成有效的
|
||||
@@ -1100,7 +1197,7 @@ namespace Serein.NodeFlow.Services
|
||||
var info = flowCallNode.ToFlowApiMethodInfo();
|
||||
if (info is not null)
|
||||
{
|
||||
flowApiMethodInfos[flowCallNode] = info;
|
||||
_flowApiMethodInfos[flowCallNode] = info;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1123,7 +1220,7 @@ namespace Serein.NodeFlow.Services
|
||||
|
||||
sb.AppendCode(1, $"public interface IFlowApiInvoke");
|
||||
sb.AppendCode(1, $"{{");
|
||||
var infos = flowApiMethodInfos.Values.ToArray();
|
||||
var infos = _flowApiMethodInfos.Values.ToArray();
|
||||
foreach (var info in infos)
|
||||
{
|
||||
var xmlDescription = $"{$"流程接口,{info.NodeModel.MethodDetails.MethodAnotherName}".ToXmlComments(2)}";
|
||||
@@ -1151,7 +1248,7 @@ namespace Serein.NodeFlow.Services
|
||||
/// <param name="sb"></param>
|
||||
private void GenerateFlowApi_ApiParamClass(StringBuilder sb)
|
||||
{
|
||||
var infos = flowApiMethodInfos.Values.ToArray();
|
||||
var infos = _flowApiMethodInfos.Values.ToArray();
|
||||
foreach (var info in infos)
|
||||
{
|
||||
sb.AppendLine(info.ToParamterClassSignature());
|
||||
|
||||
@@ -230,18 +230,27 @@ namespace Serein.Script
|
||||
else
|
||||
{
|
||||
Append($"\"");
|
||||
foreach (var s in sp)
|
||||
for (int index = 0; index < sp.Length; index++)
|
||||
{
|
||||
string? s = sp[index];
|
||||
var content = EscapeForCSharpString(s);
|
||||
if(OperatingSystem.IsWindows())
|
||||
if(index == 0)
|
||||
{
|
||||
Append($"\\r\\n{content}");
|
||||
Append(content);
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
else
|
||||
{
|
||||
Append($"\\n{content}");
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
Append($"\\r\\n{content}");
|
||||
}
|
||||
else if (OperatingSystem.IsLinux())
|
||||
{
|
||||
Append($"\\n{content}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
Append($"\"");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user