mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
376 lines
14 KiB
C#
376 lines
14 KiB
C#
|
|
using Serein.Library;
|
|||
|
|
using Serein.Library.Api;
|
|||
|
|
using Serein.Library.Utils;
|
|||
|
|
using Serein.NodeFlow.Model;
|
|||
|
|
using Serein.NodeFlow.Services;
|
|||
|
|
using Serein.NodeFlow.Tool;
|
|||
|
|
using System;
|
|||
|
|
using System.Collections;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using static Microsoft.CodeAnalysis.CSharp.SyntaxTokenParser;
|
|||
|
|
|
|||
|
|
namespace Serein.NodeFlow.Env
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
internal class FlowControl : IFlowControl
|
|||
|
|
{
|
|||
|
|
private readonly IFlowEnvironment flowEnvironment;
|
|||
|
|
private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
|
|||
|
|
private readonly FlowLibraryService flowLibraryService;
|
|||
|
|
private readonly FlowOperationService flowOperationService;
|
|||
|
|
private readonly FlowModelService flowModelService;
|
|||
|
|
private readonly UIContextOperation UIContextOperation;
|
|||
|
|
|
|||
|
|
public FlowControl(IFlowEnvironment flowEnvironment,
|
|||
|
|
IFlowEnvironmentEvent flowEnvironmentEvent,
|
|||
|
|
FlowLibraryService flowLibraryService,
|
|||
|
|
FlowOperationService flowOperationService,
|
|||
|
|
FlowModelService flowModelService,
|
|||
|
|
UIContextOperation UIContextOperation)
|
|||
|
|
{
|
|||
|
|
this.flowEnvironment = flowEnvironment;
|
|||
|
|
this.flowEnvironmentEvent = flowEnvironmentEvent;
|
|||
|
|
this.flowLibraryService = flowLibraryService;
|
|||
|
|
this.flowOperationService = flowOperationService;
|
|||
|
|
this.flowModelService = flowModelService;
|
|||
|
|
this.UIContextOperation = UIContextOperation;
|
|||
|
|
contexts = new ObjectPool<IDynamicContext>(() => new DynamicContext(flowEnvironment));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private ObjectPool<IDynamicContext> contexts;
|
|||
|
|
private FlowWorkManagement flowWorkManagement;
|
|||
|
|
private ISereinIOC sereinIOC;
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 如果全局触发器还在运行,则为 Running 。
|
|||
|
|
/// </summary>
|
|||
|
|
private RunState FlipFlopState = RunState.NoStart;
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 异步运行
|
|||
|
|
/// </summary>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
|||
|
|
{
|
|||
|
|
#region 校验参数
|
|||
|
|
HashSet<string> guids = new HashSet<string>();
|
|||
|
|
bool isBreak = false;
|
|||
|
|
foreach (var canvasGuid in canvasGuids)
|
|||
|
|
{
|
|||
|
|
if (guids.Contains(canvasGuid))
|
|||
|
|
{
|
|||
|
|
flowEnvironment.WriteLine(InfoType.WARN, $"画布重复,停止运行。{canvasGuid}");
|
|||
|
|
isBreak = true;
|
|||
|
|
}
|
|||
|
|
else if (!flowModelService.ContainsCanvasModel(canvasGuid))
|
|||
|
|
{
|
|||
|
|
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{canvasGuid}");
|
|||
|
|
isBreak = true;
|
|||
|
|
}
|
|||
|
|
else if (!flowModelService.IsExsitNodeOnCanvas(canvasGuid))
|
|||
|
|
{
|
|||
|
|
SereinEnv.WriteLine(InfoType.WARN, $"画布没有节点,停止运行。{canvasGuid}");
|
|||
|
|
isBreak = true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
guids.Add(canvasGuid);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
if (isBreak)
|
|||
|
|
{
|
|||
|
|
guids.Clear();
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
|
|||
|
|
#region 初始化每个画布的数据,转换为流程任务
|
|||
|
|
Dictionary<string, FlowTask> flowTasks = [];
|
|||
|
|
foreach (var guid in guids)
|
|||
|
|
{
|
|||
|
|
if (!flowModelService.TryGetCanvasModel(guid, out var canvasModel))
|
|||
|
|
{
|
|||
|
|
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
var ft = new FlowTask();
|
|||
|
|
ft.GetNodes = () => flowModelService.GetAllNodeModel(guid);
|
|||
|
|
if (canvasModel.StartNode?.Guid is null)
|
|||
|
|
{
|
|||
|
|
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}");
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
ft.GetStartNode = () => canvasModel.StartNode;
|
|||
|
|
flowTasks.Add(guid, ft);
|
|||
|
|
}
|
|||
|
|
#endregion
|
|||
|
|
|
|||
|
|
sereinIOC.Reset();
|
|||
|
|
sereinIOC.Register<IFlowEnvironment>(() => flowEnvironment);
|
|||
|
|
sereinIOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
|||
|
|
|
|||
|
|
var flowTaskOptions = new FlowWorkOptions
|
|||
|
|
{
|
|||
|
|
Environment = flowEnvironment, // 流程
|
|||
|
|
Flows = flowTasks,
|
|||
|
|
FlowContextPool = contexts, // 上下文对象池
|
|||
|
|
AutoRegisterTypes = flowLibraryService.GetaAutoRegisterType(), // 需要自动实例化的类型
|
|||
|
|
InitMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Init),
|
|||
|
|
LoadMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Loading),
|
|||
|
|
ExitMds = flowLibraryService.GetMdsOnFlowStart(NodeType.Exit),
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
flowWorkManagement = new FlowWorkManagement(flowTaskOptions);
|
|||
|
|
var cts = new CancellationTokenSource();
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
var t = await flowWorkManagement.RunAsync(cts.Token);
|
|||
|
|
}
|
|||
|
|
catch (Exception ex)
|
|||
|
|
{
|
|||
|
|
SereinEnv.WriteLine(ex);
|
|||
|
|
}
|
|||
|
|
finally
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}"); ;
|
|||
|
|
}
|
|||
|
|
flowTaskOptions = null;
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 从选定节点开始运行
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="startNodeGuid"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
var flowTaskOptions = new FlowWorkOptions
|
|||
|
|
{
|
|||
|
|
Environment = flowEnvironment, // 流程
|
|||
|
|
FlowContextPool = contexts, // 上下文对象池
|
|||
|
|
};
|
|||
|
|
var flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
|
|||
|
|
|
|||
|
|
if (true || flowEnvironment.FlowState == RunState.Running || FlipFlopState == RunState.Running)
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
if (!flowModelService.TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
await flowTaskManagement.StartFlowInSelectNodeAsync(nodeModel);
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
else
|
|||
|
|
{
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/*/// <summary>
|
|||
|
|
/// 单独运行一个节点
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeGuid"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
|||
|
|
{
|
|||
|
|
object result = Unit.Default;
|
|||
|
|
if (this.NodeModels.TryGetValue(nodeGuid, out var model))
|
|||
|
|
{
|
|||
|
|
CancellationTokenSource cts = new CancellationTokenSource();
|
|||
|
|
result = await model.ExecutingAsync(context, cts.Token);
|
|||
|
|
cts?.Cancel();
|
|||
|
|
}
|
|||
|
|
return result;
|
|||
|
|
}*/
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 结束流程
|
|||
|
|
/// </summary>
|
|||
|
|
public Task<bool> ExitFlowAsync()
|
|||
|
|
{
|
|||
|
|
flowWorkManagement?.Exit();
|
|||
|
|
UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnFlowRunComplete(new FlowEventArgs()));
|
|||
|
|
sereinIOC.Reset();
|
|||
|
|
flowWorkManagement = null;
|
|||
|
|
GC.Collect();
|
|||
|
|
return Task.FromResult(true);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 激活全局触发器
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeGuid"></param>
|
|||
|
|
public void ActivateFlipflopNode(string nodeGuid)
|
|||
|
|
{
|
|||
|
|
/*if (!TryGetNodeModel(nodeGuid, out var nodeModel))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (nodeModel is null) return;
|
|||
|
|
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
|||
|
|
{
|
|||
|
|
if (FlowState != RunState.Completion
|
|||
|
|
&& flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点
|
|||
|
|
{
|
|||
|
|
_ = flowTaskManagement.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}*/
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 关闭全局触发器
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeGuid"></param>
|
|||
|
|
public void TerminateFlipflopNode(string nodeGuid)
|
|||
|
|
{
|
|||
|
|
/* if (!TryGetNodeModel(nodeGuid, out var nodeModel))
|
|||
|
|
{
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
if (nodeModel is null) return;
|
|||
|
|
if (flowTaskManagement is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器
|
|||
|
|
{
|
|||
|
|
flowTaskManagement.TerminateGlobalFlipflopRuning(flipflopNode);
|
|||
|
|
}*/
|
|||
|
|
}
|
|||
|
|
/// <inheritdoc/>
|
|||
|
|
public void UseExternalIOC(ISereinIOC ioc)
|
|||
|
|
{
|
|||
|
|
this.sereinIOC = ioc; // 设置IOC容器
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 启动器调用,运行到某个节点时触发了监视对象的更新(对象预览视图将会自动更新)
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeGuid"></param>
|
|||
|
|
/// <param name="monitorData"></param>
|
|||
|
|
/// <param name="sourceType"></param>
|
|||
|
|
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
|||
|
|
{
|
|||
|
|
flowEnvironmentEvent.OnMonitorObjectChanged(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 启动器调用,节点触发了中断。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeGuid">节点</param>
|
|||
|
|
/// <param name="expression">表达式</param>
|
|||
|
|
/// <param name="type">类型,0用户主动的中断,1表达式中断</param>
|
|||
|
|
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
|||
|
|
{
|
|||
|
|
flowEnvironmentEvent.OnInterruptTriggered(new InterruptTriggerEventArgs(nodeGuid, expression, type));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
#region 流程接口调用
|
|||
|
|
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 调用流程接口,将返回 FlowResult.Value。如果需要 FlowResult 对象,请使用该方法的泛型版本。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="apiGuid">流程接口节点Guid</param>
|
|||
|
|
/// <param name="param">调用时入参参数</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|||
|
|
public async Task<object> ApiInvokeAsync(string apiGuid, object[] param)
|
|||
|
|
{
|
|||
|
|
if (sereinIOC is null)
|
|||
|
|
{
|
|||
|
|
sereinIOC = flowEnvironment.IOC;
|
|||
|
|
}
|
|||
|
|
if (!flowModelService.TryGetNodeModel(apiGuid, out var nodeModel))
|
|||
|
|
{
|
|||
|
|
throw new ArgumentNullException($"不存在流程接口:{apiGuid}");
|
|||
|
|
}
|
|||
|
|
if (nodeModel is not SingleFlowCallNode flowCallNode)
|
|||
|
|
{
|
|||
|
|
throw new ArgumentNullException($"目标节点并非流程接口:{apiGuid}");
|
|||
|
|
}
|
|||
|
|
var context = contexts.Allocate();
|
|||
|
|
CancellationTokenSource cts = new CancellationTokenSource();
|
|||
|
|
var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token);
|
|||
|
|
return flowResult.Value;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 调用流程接口,泛型类型为 FlowResult 时,将返回 FlowResult 对象。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <typeparam name="TResult"></typeparam>
|
|||
|
|
/// <param name="apiGuid">流程接口节点Guid</param>
|
|||
|
|
/// <param name="param">调用时入参参数</param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
/// <exception cref="ArgumentNullException"></exception>
|
|||
|
|
public async Task<TResult> ApiInvokeAsync<TResult>(string apiGuid, object[] param)
|
|||
|
|
{
|
|||
|
|
if (sereinIOC is null)
|
|||
|
|
{
|
|||
|
|
sereinIOC = flowEnvironment.IOC;
|
|||
|
|
}
|
|||
|
|
if (!flowModelService.TryGetNodeModel(apiGuid, out var nodeModel))
|
|||
|
|
{
|
|||
|
|
throw new ArgumentNullException($"不存在流程接口:{apiGuid}");
|
|||
|
|
}
|
|||
|
|
if (nodeModel is not SingleFlowCallNode flowCallNode)
|
|||
|
|
{
|
|||
|
|
throw new ArgumentNullException($"目标节点并非流程接口:{apiGuid}");
|
|||
|
|
}
|
|||
|
|
var context = contexts.Allocate();
|
|||
|
|
CancellationTokenSource cts = new CancellationTokenSource();
|
|||
|
|
var flowResult = await flowCallNode.StartFlowAsync(context, cts.Token);
|
|||
|
|
|
|||
|
|
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}]。");
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
private bool IsHasSuccessorNodes(IFlowNode nodeModel)
|
|||
|
|
{
|
|||
|
|
var nextTypes = new[]
|
|||
|
|
{
|
|||
|
|
ConnectionInvokeType.Upstream,
|
|||
|
|
ConnectionInvokeType.IsSucceed,
|
|||
|
|
ConnectionInvokeType.IsFail,
|
|||
|
|
ConnectionInvokeType.IsError
|
|||
|
|
};
|
|||
|
|
foreach (var type in nextTypes)
|
|||
|
|
{
|
|||
|
|
if (nodeModel.SuccessorNodes.TryGetValue(type, out var nextNodes))
|
|||
|
|
{
|
|||
|
|
if(nextNodes.Count > 0)
|
|||
|
|
{
|
|||
|
|
return true;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return false;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
#endregion
|
|||
|
|
}
|
|||
|
|
}
|