实现了多画布下,节点的复制粘贴功能

This commit is contained in:
fengjiayi
2025-05-27 18:32:40 +08:00
parent 7ad6041be6
commit 7848af0363
53 changed files with 1187 additions and 499 deletions

View File

@@ -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>

View File

@@ -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);
}

View File

@@ -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,

View File

@@ -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
{

View File

@@ -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);

View File

@@ -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>
/// 每个画布需要启用的节点

View File

@@ -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);