完成mvvm模式下,画布、节点编辑的基本重构

This commit is contained in:
fengjiayi
2025-05-26 23:55:23 +08:00
parent ba74facf7d
commit 7ad6041be6
38 changed files with 2056 additions and 436 deletions

View File

@@ -5,6 +5,10 @@
/// </summary>
public static class EnvMsgTheme
{
/// <summary>
/// 尝试保存项目
/// </summary>
public const string SaveProject = nameof(SaveProject);
/// <summary>
/// 获取远程环境信息
/// </summary>

View File

@@ -301,7 +301,7 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 运行环境加载的画布集合
/// </summary>
private Dictionary<string, FlowCanvasModel> FlowCanvass { get; } = [];
private Dictionary<string, FlowCanvasDetails> FlowCanvass { get; } = [];
/// <summary>
/// 存放触发器节点(运行时全部调用)
@@ -309,35 +309,35 @@ namespace Serein.NodeFlow.Env
private List<SingleFlipflopNode> FlipflopNodes { get; } = [];
/*
/// <summary>
/// 起始节点私有属性
/// </summary>
private NodeModelBase? _startNode = null;
/// <summary>
/// 起始节点私有属性
/// </summary>
private NodeModelBase? _startNode = null;
/// <summary>
/// 起始节点
/// </summary>
private NodeModelBase? StartNode
{
get
{
return _startNode;
}
set
{
if (value is null)
{
return;
}
if (_startNode is not null)
{
_startNode.IsStart = false;
}
value.IsStart = true;
_startNode = value;
}
}
/// <summary>
/// 起始节点
/// </summary>
private NodeModelBase? StartNode
{
get
{
return _startNode;
}
set
{
if (value is null)
{
return;
}
if (_startNode is not null)
{
_startNode.IsStart = false;
}
value.IsStart = true;
_startNode = value;
}
}*/
/// <summary>
/// 流程任务管理
@@ -373,12 +373,11 @@ namespace Serein.NodeFlow.Env
IOC.Reset();
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
var flowTaskOptions = new FlowWorkLibrary
var flowTaskOptions = new FlowWorkOptions
{
Environment = this,
FlowContextPool = new ObjectPool<IDynamicContext>(() => new DynamicContext(this)),
Nodes = NodeModels.Values.ToList(),
//Nodes = NodeModels.Values.ToList(),
AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(),
InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading),
@@ -534,7 +533,8 @@ namespace Serein.NodeFlow.Env
/// </summary>
public void SaveProject()
{
OnProjectSaving?.Invoke(new ProjectSavingEventArgs());
var project = GetProjectInfoAsync().GetAwaiter().GetResult();
OnProjectSaving?.Invoke(new ProjectSavingEventArgs(project));
}
/// <summary>
@@ -562,7 +562,7 @@ namespace Serein.NodeFlow.Env
_ = Task.Run( async () =>
{
await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息
await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
//await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
});
}
@@ -615,16 +615,17 @@ namespace Serein.NodeFlow.Env
/// 序列化当前项目的依赖信息、节点信息
/// </summary>
/// <returns></returns>
public Task<SereinProjectData> GetProjectInfoAsync()
public async Task<SereinProjectData> GetProjectInfoAsync()
{
var projectData = new SereinProjectData()
{
Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(),
Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
Canvass = FlowCanvass.Values.Select(canvas => canvas.ToInfo()).ToArray(),
//StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
};
return Task.FromResult(projectData);
return projectData;
}
@@ -776,22 +777,22 @@ namespace Serein.NodeFlow.Env
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <returns></returns>
public Task<FlowCanvasInfo> CreateCanvasAsync(string canvasName, int width, int height)
public async Task<FlowCanvasDetailsInfo> CreateCanvasAsync(string canvasName, int width, int height)
{
var model = new FlowCanvasModel(this)
var model = new FlowCanvasDetails(this)
{
Guid = Guid.NewGuid().ToString(),
Height = height,
Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图 {_addCanvasCount++}",
Width = height,
Width = width,
Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}",
};
FlowCanvass.Add(model.Guid, model);
UIContextOperation.Invoke(() =>
await UIContextOperation.InvokeAsync(() =>
{
OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model));
});
var info = model.ToInfo();
return Task.FromResult(info);
return info;
}
/// <summary>
@@ -799,15 +800,29 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <param name="canvasGuid">画布Guid</param>
/// <returns></returns>
public Task<bool> RemoveCanvasAsync(string canvasGuid)
public async Task<bool> RemoveCanvasAsync(string canvasGuid)
{
if (!FlowCanvass.TryGetValue(canvasGuid, out var model))
{
return Task.FromResult(false);
return false;
}
var count = NodeModels.Values.Count(node => node.CanvasGuid.Equals(canvasGuid));
return Task.FromResult(count == 0);
if(count > 0)
{
SereinEnv.WriteLine(InfoType.WARN, "无法删除具有节点的画布");
return false;
}
if (FlowCanvass.Remove(canvasGuid))
{
await UIContextOperation.InvokeAsync(() =>
{
OnCanvasRemove.Invoke(new CanvasRemoveEventArgs(canvasGuid));
});
return true;
}
return false;
}
@@ -981,7 +996,7 @@ namespace Serein.NodeFlow.Env
PositionOfUI position,
MethodDetailsInfo? methodDetailsInfo = null)
{
if (!FlowCanvass.ContainsKey(canvasGuid))
if (!TryGetCanvasModel(canvasGuid,out var cavnasModel))
{
return Task.FromResult<NodeInfo>(null);
}
@@ -1005,16 +1020,17 @@ namespace Serein.NodeFlow.Env
}
TryAddNode(nodeModel);
nodeModel.Position = position;
nodeModel.CanvasGuid = canvasGuid; // 设置所属于的画布
nodeModel.Position = position; // 设置位置
// 通知UI更改
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position)));
// 因为需要UI先布置了元素才能通知UI变更特效
// 如果不存在流程起始控件,默认设置为流程起始控件
if (StartNode is null)
if (cavnasModel.StartNode is null)
{
SetStartNode(canvasGuid, nodeModel);
SetStartNode(cavnasModel, nodeModel);
}
var nodeInfo = nodeModel.ToInfo();
return Task.FromResult(nodeInfo);
@@ -1392,15 +1408,16 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 设置起点控件
/// </summary>
/// <param name="newNodeGuid"></param>
/// <param name="canvasGuid">画布</param>
/// <param name="newNodeGuid">节点Guid</param>
public Task<string> SetStartNodeAsync(string canvasGuid, string newNodeGuid)
{
if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel))
if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel))
{
return Task.FromResult(StartNode?.Guid ?? string.Empty);
return Task.FromResult(canvasModel.StartNode ?? string.Empty);
}
SetStartNode(canvasGuid, newStartNodeModel);
return Task.FromResult(StartNode?.Guid ?? string.Empty);
SetStartNode(canvasModel, newStartNodeModel);
return Task.FromResult(canvasModel.StartNode ?? string.Empty);
}
/// <summary>
@@ -1512,9 +1529,25 @@ namespace Serein.NodeFlow.Env
}
/// <summary>
/// 从Guid获取画布
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails)
{
if (string.IsNullOrEmpty(nodeGuid))
{
canvasDetails = null;
return false;
}
return FlowCanvass.TryGetValue(nodeGuid, out canvasDetails) && canvasDetails is not null;
}
/// <summary>
/// Guid 转 NodeModel
/// Guid获取节点
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
@@ -1913,15 +1946,11 @@ namespace Serein.NodeFlow.Env
/// </summary>
/// <param name="newStartNode"></param>
/// <param name="oldStartNode"></param>
private void SetStartNode(string canvasGuid, NodeModelBase newStartNode)
private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode)
{
if (!FlowCanvass.ContainsKey(canvasGuid))
{
return;
}
var oldNodeGuid = StartNode?.Guid;
StartNode = newStartNode;
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, StartNode.Guid)));
var oldNodeGuid = cavnasModel.StartNode;
cavnasModel.StartNode = newStartNode.Guid;
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode)));
//if (OperatingSystem.IsWindows())
//{

View File

@@ -238,7 +238,7 @@ namespace Serein.NodeFlow.Env
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <returns></returns>
public async Task<FlowCanvasInfo> CreateCanvasAsync(string canvasName, int width, int height)
public async Task<FlowCanvasDetailsInfo> CreateCanvasAsync(string canvasName, int width, int height)
{
return await currentFlowEnvironment.CreateCanvasAsync(canvasName, width, height);
}

View File

@@ -164,7 +164,7 @@ namespace Serein.NodeFlow.Env
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateCanvas, IsReturnValue = false)]
public void CreateCanvas([UseMsgId] string msgId, [UseData] FlowCanvasInfo canvasInfo)
public void CreateCanvas([UseMsgId] string msgId, [UseData] FlowCanvasDetailsInfo canvasInfo)
{
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, canvasInfo);
}

View File

@@ -312,7 +312,7 @@ namespace Serein.NodeFlow.Env
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateCanvas, IsReturnValue = false)]
public async Task<FlowCanvasInfo> CreateCanvas(string canvasName, int width, int height)
public async Task<FlowCanvasDetailsInfo> CreateCanvas(string canvasName, int width, int height)
{
var canvasInfo = await environment.CreateCanvasAsync(canvasName, width, height); // 监听到客户端创建节点的请求
return canvasInfo;

View File

@@ -131,11 +131,28 @@ namespace Serein.NodeFlow.Env
}
/// <summary>
/// 保存项目
/// 远程环境下保存项目
/// </summary>
public void SaveProject()
{
OnProjectSaving?.Invoke(new ProjectSavingEventArgs());
_ = Task.Run(async () =>
{
// 保存项目
var result = await msgClient.SendAndWaitDataAsync<SereinProjectData>(EnvMsgTheme.SaveProject);
if (result is not null)
{
OnProjectSaving?.Invoke(new ProjectSavingEventArgs(result));
}
});
// 获取远程环境
//var projectData = new SereinProjectData()
//{
// Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(),
// Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
// StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
//};
}
@@ -167,14 +184,26 @@ namespace Serein.NodeFlow.Env
}
#endregion
var nodeInfos = flowEnvInfo.Project.Nodes.ToList();
LoadNodeInfos(nodeInfos); // 加载节点
var canvasGuid = nodeInfos.FirstOrDefault(item => item.Guid == flowEnvInfo.Project.StartNode)?.CanvasGuid;
if (!string.IsNullOrEmpty(canvasGuid))
// 加载画布
foreach (var info in flowEnvInfo.Project.Canvass)
{
_ = SetStartNodeAsync(canvasGuid, flowEnvInfo.Project.StartNode); // 设置流程起点
var canvasModel = new FlowCanvasDetails(this);
canvasModel.LoadInfo(info);
var e = new CanvasCreateEventArgs(canvasModel);
OnCanvasCreate?.Invoke(e);
}
// 加载节点
var nodeInfos = flowEnvInfo.Project.Nodes.ToList();
LoadNodeInfos(nodeInfos);
// 设置每个画布的起始节点
foreach (var info in flowEnvInfo.Project.Canvass)
{
_ = SetStartNodeAsync(info.Guid, info.StartNode); // 设置流程起点
}
UIContextOperation?.Invoke(() =>
{
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成
@@ -427,9 +456,9 @@ namespace Serein.NodeFlow.Env
/// <param name="width">宽度</param>
/// <param name="height">高度</param>
/// <returns></returns>
public async Task<FlowCanvasInfo> CreateCanvasAsync(string canvasName, int width, int height)
public async Task<FlowCanvasDetailsInfo> CreateCanvasAsync(string canvasName, int width, int height)
{
var info = await msgClient.SendAndWaitDataAsync<FlowCanvasInfo>(EnvMsgTheme.CreateCanvas, new
var info = await msgClient.SendAndWaitDataAsync<FlowCanvasDetailsInfo>(EnvMsgTheme.CreateCanvas, new
{
canvasName,
width,
@@ -437,7 +466,7 @@ namespace Serein.NodeFlow.Env
});
if (info is not null)
{
var model = new FlowCanvasModel(this)
var model = new FlowCanvasDetails(this)
{
Guid = info.Guid,
Height = info.Height,
@@ -506,6 +535,7 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 设置远程环境的流程起点节点
/// </summary>
/// <param name="canvasGuid">节点画布</param>
/// <param name="nodeGuid">尝试设置为起始节点的节点Guid</param>
/// <returns>被设置为起始节点的Guid</returns>
public async Task<string> SetStartNodeAsync(string canvasGuid, string nodeGuid)

View File

@@ -6,6 +6,7 @@ using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks.Dataflow;
using System.Xml.Linq;
namespace Serein.NodeFlow
@@ -20,25 +21,24 @@ namespace Serein.NodeFlow
/// </summary>
private ConcurrentDictionary<SingleFlipflopNode, CancellationTokenSource> dictGlobalFlipflop = [];
/// <summary>
/// 结束运行时需要执行的方法
/// </summary>
private Func<Task>? ExitAction { get; set; }
/// <summary>
/// 初始化选项
/// </summary>
public FlowWorkLibrary WorkLibrary { get; }
public FlowWorkOptions WorkOptions { get; }
/// <summary>
/// 流程任务管理
/// </summary>
/// <param name="library"></param>
public FlowWorkManagement(FlowWorkLibrary library)
/// <param name="options"></param>
public FlowWorkManagement(FlowWorkOptions options)
{
WorkLibrary = library;
WorkOptions = options;
}
@@ -48,43 +48,83 @@ namespace Serein.NodeFlow
/// <returns></returns>
public async Task<bool> RunAsync(CancellationToken token)
{
NodeModelBase? startNode = WorkLibrary.Nodes.FirstOrDefault(node => node.IsStart);
if (startNode is null)
#region 退
List<NodeModelBase> nodes = new List<NodeModelBase>();
foreach (var item in WorkOptions.Flows.Values)
{
var temp = item.GetNodes();
nodes.AddRange(temp);
}
if (!RegisterAllType(nodes))
{
return false;
}
#endregion
#region InitLoad事件
if (!RegisterAllType())
{
return false;
};
var initState = await TryInit();
if (!initState)
{
return false;
};
}
;
var loadState = await TryLoadAsync();
if (!loadState)
{
return false;
};
var task = CallFlipflopNode();
await CallStartNode(startNode);
await task;
}
;
#endregion
// 开始调用流程
foreach (var kvp in WorkOptions.Flows)
{
var guid = kvp.Key;
var flow = kvp.Value;
var flowNodes = flow.GetNodes();
// 找到流程的起始节点,开始运行
NodeModelBase? startNode = flowNodes.FirstOrDefault(node => node.IsStart);
if (startNode is null)
{
return false;
}
// 是否后台运行当前画布流程
if (flow.IsTaskAsync)
{
_ = Task.Run(async () => await CallStartNode(startNode));
}
else
{
await CallStartNode(startNode);
}
var flipflopTasks = CallFlipflopNode(flow); // 获取所有触发器异步任务
_ = Task.Run(async () => await flipflopTasks); // 后台调用流程中的触发器
}
// 等待流程运行完成
await CallExit();
return true;
}
#region
private bool RegisterAllType()
/// <summary>
/// 初始化节点所需的所有类型
/// </summary>
/// <returns></returns>
private bool RegisterAllType(List<NodeModelBase> nodes)
{
var env = WorkLibrary.Environment;
var nodeMds = WorkLibrary.Nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息
var env = WorkOptions.Environment;
var nodeMds = nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息
var allMds = new List<MethodDetails>();
allMds.AddRange(nodeMds.Where(md => md?.ActingInstanceType is not null));
allMds.AddRange(WorkLibrary.InitMds.Where(md => md?.ActingInstanceType is not null));
allMds.AddRange(WorkLibrary.LoadMds.Where(md => md?.ActingInstanceType is not null));
allMds.AddRange(WorkLibrary.ExitMds.Where(md => md?.ActingInstanceType is not null));
allMds.AddRange(WorkOptions.InitMds.Where(md => md?.ActingInstanceType is not null));
allMds.AddRange(WorkOptions.LoadMds.Where(md => md?.ActingInstanceType is not null));
allMds.AddRange(WorkOptions.ExitMds.Where(md => md?.ActingInstanceType is not null));
var isSuccessful = true;
foreach (var md in allMds)
{
@@ -114,10 +154,10 @@ namespace Serein.NodeFlow
private async Task<bool> TryInit()
{
var env = WorkLibrary.Environment;
var initMds = WorkLibrary.InitMds;
var pool = WorkLibrary.FlowContextPool;
var ioc = WorkLibrary.Environment.IOC;
var env = WorkOptions.Environment;
var initMds = WorkOptions.InitMds;
var pool = WorkOptions.FlowContextPool;
var ioc = WorkOptions.Environment.IOC;
foreach (var md in initMds) // 初始化
{
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
@@ -136,10 +176,10 @@ namespace Serein.NodeFlow
}
private async Task<bool> TryLoadAsync()
{
var env = WorkLibrary.Environment;
var loadMds = WorkLibrary.LoadMds;
var pool = WorkLibrary.FlowContextPool;
var ioc = WorkLibrary.Environment.IOC;
var env = WorkOptions.Environment;
var loadMds = WorkOptions.LoadMds;
var pool = WorkOptions.FlowContextPool;
var ioc = WorkOptions.Environment.IOC;
foreach (var md in loadMds) // 加载时
{
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
@@ -159,10 +199,10 @@ namespace Serein.NodeFlow
}
private async Task<bool> CallExit()
{
var env = WorkLibrary.Environment;
var mds = WorkLibrary.ExitMds;
var pool = WorkLibrary.FlowContextPool;
var ioc = WorkLibrary.Environment.IOC;
var env = WorkOptions.Environment;
var mds = WorkOptions.ExitMds;
var pool = WorkOptions.FlowContextPool;
var ioc = WorkOptions.Environment.IOC;
ioc.Run<FlowInterruptTool>(fit => fit.CancelAllTrigger());// 取消所有中断
foreach (var md in mds) // 结束时
@@ -186,10 +226,10 @@ namespace Serein.NodeFlow
return isSuccessful;
}
private Task CallFlipflopNode()
private Task CallFlipflopNode(FlowTask flow)
{
var env = WorkLibrary.Environment;
var flipflopNodes = WorkLibrary.Nodes.Where(item => item is SingleFlipflopNode node
var env = WorkOptions.Environment;
var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node
&& !node.IsStart
&& node.DebugSetting.IsEnable
&& node.NotExitPreviousNode())
@@ -206,10 +246,16 @@ namespace Serein.NodeFlow
}
return Task.CompletedTask;
}
/// <summary>
/// 从某一个节点开始执行
/// </summary>
/// <param name="startNode"></param>
/// <returns></returns>
private async Task CallStartNode(NodeModelBase startNode)
{
var pool = WorkLibrary.FlowContextPool;
var token = WorkLibrary.CancellationTokenSource.Token;
var pool = WorkOptions.FlowContextPool;
var token = WorkOptions.CancellationTokenSource.Token;
var context = pool.Allocate();
await startNode.StartFlowAsync(context, token);
context.Exit();
@@ -227,9 +273,9 @@ namespace Serein.NodeFlow
/// <returns></returns>
public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode)
{
var pool = WorkLibrary.FlowContextPool;
var pool = WorkOptions.FlowContextPool;
var context = pool.Allocate();
var token = WorkLibrary.CancellationTokenSource.Token;
var token = WorkOptions.CancellationTokenSource.Token;
await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
context.Reset();
pool.Free(context);
@@ -294,7 +340,7 @@ namespace Serein.NodeFlow
CancellationToken singleToken)
{
var pool = WorkLibrary.FlowContextPool;
var pool = WorkOptions.FlowContextPool;
while (!singleToken.IsCancellationRequested && !singleToken.IsCancellationRequested)
{
try

View File

@@ -9,10 +9,29 @@ using System.Threading.Tasks;
namespace Serein.NodeFlow
{
public class FlowTask
{
/// <summary>
/// 是否异步启动流程
/// </summary>
public bool IsTaskAsync { get; set; }
/// <summary>
/// 流程起始节点
/// </summary>
public Func<NodeModelBase> GetStartNode { get; set; }
/// <summary>
/// 获取当前画布流程的所有节点
/// </summary>
public Func<List<NodeModelBase>> GetNodes { get; set; }
}
/// <summary>
/// 节点任务执行依赖
/// </summary>
public class FlowWorkLibrary()
public class FlowWorkOptions()
{
/// <summary>
/// 流程运行环境
@@ -29,14 +48,21 @@ namespace Serein.NodeFlow
/// </summary>
public Serein.Library.Utils.ObjectPool<IDynamicContext> FlowContextPool { get; set; }
/// <summary>
/// 每个画布需要启用的节点
/// </summary>
public Dictionary<string, FlowTask> Flows { get; set; }
/// <summary>
/// 当前任务加载的所有节点
/// </summary>
public List<NodeModelBase> Nodes { get; set; }// = nodes;
//public List<NodeModelBase> Nodes { get; set; }// = nodes;
/// <summary>
/// 需要注册的类型
/// </summary>
public Dictionary<RegisterSequence, List<Type>> AutoRegisterTypes { get; set; } //= autoRegisterTypes;
/// <summary>
/// 初始化时需要的方法
/// </summary>