mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-04 07:16:35 +08:00
实现了多画布下,节点的复制粘贴功能
This commit is contained in:
@@ -368,27 +368,87 @@ namespace Serein.NodeFlow.Env
|
||||
/// 异步运行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartFlowAsync()
|
||||
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))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布重复,停止运行。{canvasGuid}");
|
||||
isBreak = true;
|
||||
}
|
||||
if (!FlowCanvass.ContainsKey(canvasGuid))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{canvasGuid}");
|
||||
isBreak = true;
|
||||
}
|
||||
var count = NodeModels.Values.Count(n => n.CanvasGuid.Equals(canvasGuid));
|
||||
if(count == 0)
|
||||
{
|
||||
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 (!TryGetCanvasModel(guid, out var canvasModel))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}");
|
||||
return false;
|
||||
}
|
||||
var ft = new FlowTask();
|
||||
ft.GetNodes = () => NodeModels.Values.Where(node => node.CanvasGuid.Equals(guid)).ToList();
|
||||
var startNodeModel = NodeModels.GetValueOrDefault(canvasModel.StartNode);
|
||||
if(startNodeModel is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}");
|
||||
return false;
|
||||
}
|
||||
ft.GetStartNode = () => startNodeModel;
|
||||
flowTasks.Add(guid, ft);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
IOC.Reset();
|
||||
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
|
||||
|
||||
var flowTaskOptions = new FlowWorkOptions
|
||||
{
|
||||
Environment = this,
|
||||
FlowContextPool = new ObjectPool<IDynamicContext>(() => new DynamicContext(this)),
|
||||
//Nodes = NodeModels.Values.ToList(),
|
||||
AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(),
|
||||
InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
|
||||
Environment = this, // 流程
|
||||
Flows = flowTasks,
|
||||
FlowContextPool = new ObjectPool<IDynamicContext>(() => new DynamicContext(this)), // 上下文对象池
|
||||
AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), // 需要自动实例化的类型
|
||||
InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
|
||||
LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading),
|
||||
ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit),
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
|
||||
var cts = new CancellationTokenSource();
|
||||
try
|
||||
{
|
||||
var t =await flowTaskManagement.RunAsync(cts.Token);
|
||||
var t = await flowTaskManagement.RunAsync(cts.Token);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -405,12 +465,13 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从选定节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
|
||||
{
|
||||
|
||||
if (flowTaskManagement is null)
|
||||
@@ -425,12 +486,6 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//var getExp = "@get .DebugSetting.IsEnable";
|
||||
//var getExpResult1 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel,out _);
|
||||
//var setExp = "@set .DebugSetting.IsEnable = false";
|
||||
//SerinExpressionEvaluator.Evaluate(setExp, nodeModel,out _);
|
||||
//var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
|
||||
|
||||
await flowTaskManagement.StartFlowInSelectNodeAsync(this, nodeModel);
|
||||
return true;
|
||||
}
|
||||
@@ -559,9 +614,22 @@ namespace Serein.NodeFlow.Env
|
||||
LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
|
||||
}
|
||||
|
||||
|
||||
|
||||
_ = Task.Run( async () =>
|
||||
{
|
||||
{
|
||||
// 加载画布
|
||||
foreach (var canvasInfo in projectData.Canvass)
|
||||
{
|
||||
LoadCanvas(canvasInfo);
|
||||
}
|
||||
await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息
|
||||
|
||||
// 加载画布
|
||||
foreach (var canvasInfo in projectData.Canvass)
|
||||
{
|
||||
await SetStartNodeAsync(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点
|
||||
}
|
||||
//await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
|
||||
});
|
||||
|
||||
@@ -779,20 +847,31 @@ namespace Serein.NodeFlow.Env
|
||||
/// <returns></returns>
|
||||
public async Task<FlowCanvasDetailsInfo> CreateCanvasAsync(string canvasName, int width, int height)
|
||||
{
|
||||
var model = new FlowCanvasDetails(this)
|
||||
var info = new FlowCanvasDetailsInfo()
|
||||
{
|
||||
Guid = Guid.NewGuid().ToString(),
|
||||
Height = height,
|
||||
Width = width,
|
||||
ViewX = 0,
|
||||
ViewY = 0,
|
||||
ScaleY = 1,
|
||||
ScaleX = 1,
|
||||
Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}",
|
||||
};
|
||||
var model = LoadCanvas(info);
|
||||
return info;
|
||||
}
|
||||
|
||||
private FlowCanvasDetails LoadCanvas(FlowCanvasDetailsInfo info)
|
||||
{
|
||||
var model = new FlowCanvasDetails(this);
|
||||
model.LoadInfo(info);
|
||||
FlowCanvass.Add(model.Guid, model);
|
||||
await UIContextOperation.InvokeAsync(() =>
|
||||
UIContextOperation.InvokeAsync(() =>
|
||||
{
|
||||
OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model));
|
||||
});
|
||||
var info = model.ToInfo();
|
||||
return info;
|
||||
return model;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -957,7 +1036,7 @@ namespace Serein.NodeFlow.Env
|
||||
#region 确定节点之间的参数调用关系
|
||||
foreach (var toNode in NodeModels.Values)
|
||||
{
|
||||
var canvasGuid = toNode.Guid;
|
||||
var canvasGuid = toNode.CanvasGuid;
|
||||
if (toNode.MethodDetails.ParameterDetailss == null)
|
||||
{
|
||||
continue;
|
||||
@@ -1727,7 +1806,7 @@ namespace Serein.NodeFlow.Env
|
||||
private bool TryAddNode(NodeModelBase nodeModel)
|
||||
{
|
||||
nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||
NodeModels[nodeModel.Guid] = nodeModel;
|
||||
NodeModels.TryAdd(nodeModel.Guid, nodeModel);
|
||||
|
||||
// 如果是触发器,则需要添加到专属集合中
|
||||
if (nodeModel is SingleFlipflopNode flipflopNode)
|
||||
@@ -1944,32 +2023,22 @@ namespace Serein.NodeFlow.Env
|
||||
/// <summary>
|
||||
/// 更改起点节点
|
||||
/// </summary>
|
||||
/// <param name="newStartNode"></param>
|
||||
/// <param name="oldStartNode"></param>
|
||||
/// <param name="cavnasModel">节点所在的画布</param>
|
||||
/// <param name="newStartNode">起始节点</param>
|
||||
private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode)
|
||||
{
|
||||
var oldNodeGuid = cavnasModel.StartNode;
|
||||
/*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel))
|
||||
{
|
||||
newStartNode.IsStart = false;
|
||||
}*/
|
||||
cavnasModel.StartNode = newStartNode.Guid;
|
||||
//newStartNode.IsStart = true;
|
||||
|
||||
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode)));
|
||||
|
||||
//if (OperatingSystem.IsWindows())
|
||||
//{
|
||||
// }
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 输出内容
|
||||
///// </summary>
|
||||
///// <param name="msg"></param>
|
||||
//private void Output(string msg)
|
||||
//{
|
||||
// if (OperatingSystem.IsWindows())
|
||||
// {
|
||||
// UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg));
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 向容器登记缓存的持久化实例
|
||||
/// </summary>
|
||||
|
||||
@@ -263,7 +263,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
||||
/// <param name="invokeType">决定了方法执行后的后继行为</param>
|
||||
public async Task<bool> ConnectInvokeNodeAsync(string canvasGuid,
|
||||
string fromNodeGuid,
|
||||
string fromNodeGuid,
|
||||
string toNodeGuid,
|
||||
JunctionType fromNodeJunctionType,
|
||||
JunctionType toNodeJunctionType,
|
||||
@@ -518,14 +518,16 @@ namespace Serein.NodeFlow.Env
|
||||
return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid);
|
||||
}
|
||||
|
||||
public async Task<bool> StartFlowAsync()
|
||||
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||
{
|
||||
return await currentFlowEnvironment.StartFlowAsync();
|
||||
return await currentFlowEnvironment.StartFlowAsync(canvasGuids);
|
||||
}
|
||||
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
|
||||
|
||||
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
|
||||
{
|
||||
return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
||||
return await currentFlowEnvironment.StartFlowFromSelectNodeAsync(startNodeGuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -173,10 +173,10 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow)]
|
||||
private async Task<object> StartAsync()
|
||||
private async Task<object> StartAsync(string[] canvasGuid)
|
||||
{
|
||||
var uiContextOperation = environment.IOC.Get<UIContextOperation>();
|
||||
var state = await environment.StartFlowAsync();
|
||||
var state = await environment.StartFlowAsync(canvasGuid);
|
||||
return new
|
||||
{
|
||||
state = state,
|
||||
@@ -191,7 +191,7 @@ namespace Serein.NodeFlow.Env
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)]
|
||||
private async Task<object> StartAsyncInSelectNode(string nodeGuid)
|
||||
{
|
||||
var state = await environment.StartAsyncInSelectNode(nodeGuid);
|
||||
var state = await environment.StartFlowFromSelectNodeAsync(nodeGuid);
|
||||
return new
|
||||
{
|
||||
state = state,
|
||||
|
||||
@@ -420,18 +420,22 @@ namespace Serein.NodeFlow.Env
|
||||
/// 启动远程环境的流程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartFlowAsync()
|
||||
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||
{
|
||||
// 远程环境下不需要UI上下文
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlow);
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlow, new
|
||||
{
|
||||
canvasGuids
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
|
||||
{
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlowInSelectNode, new
|
||||
{
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Serein.NodeFlow
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 初始化啊
|
||||
/// 初始化
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> RunAsync(CancellationToken token)
|
||||
@@ -85,22 +85,19 @@ namespace Serein.NodeFlow
|
||||
var flowNodes = flow.GetNodes();
|
||||
|
||||
// 找到流程的起始节点,开始运行
|
||||
NodeModelBase? startNode = flowNodes.FirstOrDefault(node => node.IsStart);
|
||||
if (startNode is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
NodeModelBase startNode = flow.GetStartNode();
|
||||
// 是否后台运行当前画布流程
|
||||
if (flow.IsTaskAsync)
|
||||
{
|
||||
_ = Task.Run(async () => await CallStartNode(startNode));
|
||||
_ = Task.Run(async () => await CallStartNode(startNode), token); // 后台调用流程中的触发器
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
await CallStartNode(startNode);
|
||||
|
||||
}
|
||||
var flipflopTasks = CallFlipflopNode(flow); // 获取所有触发器异步任务
|
||||
_ = Task.Run(async () => await flipflopTasks); // 后台调用流程中的触发器
|
||||
_ = Task.Run(async () => await CallFlipflopNode(flow), token); // 后台调用流程中的触发器
|
||||
}
|
||||
|
||||
// 等待流程运行完成
|
||||
@@ -226,7 +223,7 @@ namespace Serein.NodeFlow
|
||||
return isSuccessful;
|
||||
}
|
||||
|
||||
private Task CallFlipflopNode(FlowTask flow)
|
||||
private async Task CallFlipflopNode(FlowTask flow)
|
||||
{
|
||||
var env = WorkOptions.Environment;
|
||||
var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node
|
||||
@@ -242,9 +239,8 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器
|
||||
});
|
||||
Task.WhenAll(tasks);
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -257,6 +253,7 @@ namespace Serein.NodeFlow
|
||||
var pool = WorkOptions.FlowContextPool;
|
||||
var token = WorkOptions.CancellationTokenSource.Token;
|
||||
var context = pool.Allocate();
|
||||
context.Reset();
|
||||
await startNode.StartFlowAsync(context, token);
|
||||
context.Exit();
|
||||
pool.Free(context);
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Serein.NodeFlow
|
||||
/// <summary>
|
||||
/// 上下文线程池
|
||||
/// </summary>
|
||||
public Serein.Library.Utils.ObjectPool<IDynamicContext> FlowContextPool { get; set; }
|
||||
public Serein.Library.Utils.ObjectPool<IDynamicContext> FlowContextPool { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 每个画布需要启用的节点
|
||||
|
||||
@@ -69,6 +69,11 @@ namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
if (!Directory.Exists(path))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"尝试加载Dll时失败,路径不存在。{path}");
|
||||
return;
|
||||
}
|
||||
foreach (var file in Directory.GetFiles(path, "*.dll"))
|
||||
{
|
||||
LoadWindowsLibrarie(file);
|
||||
|
||||
Reference in New Issue
Block a user