From 5b0ba84fd66f0e1d1f5aadc1b806449cb39f811e Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Tue, 24 Dec 2024 22:23:53 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BB=8E=E8=8A=82=E7=82=B9Model=E8=A7=A3?=
=?UTF-8?q?=E8=80=A6=E5=87=BA=E5=AE=B9=E5=99=A8=E6=8E=A5=E5=8F=A3=EF=BC=8C?=
=?UTF-8?q?=E9=87=8D=E6=96=B0=E8=AE=BE=E8=AE=A1=E4=BA=86=E8=8A=82=E7=82=B9?=
=?UTF-8?q?=E7=9A=84=E4=BF=9D=E5=AD=98=E3=80=81=E5=8A=A0=E8=BD=BD=E3=80=82?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
FlowStartTool/FlowEnv.cs | 2 +-
Library/Api/IFlowEnvironment.cs | 100 ++-
Library/Api/INodeContainer.cs | 31 +
Library/FlowNode/NodeModelBaseData.cs | 28 +-
Library/FlowNode/NodeModelBaseFunc.cs | 2 +
Library/FlowNode/ParameterDetails.cs | 2 +-
Library/FlowNode/SereinProjectData.cs | 9 +-
.../Handle/WebSocketMsgHandleHelper.cs | 4 +-
.../SerinExpressionEvaluator.cs | 10 +-
NodeFlow/Env/EnvMsgTheme.cs | 5 +
NodeFlow/Env/FlowEnvironment.cs | 333 +++-------
NodeFlow/Env/FlowEnvironmentDecorator.cs | 24 +
NodeFlow/Env/MsgControllerOfClient.cs | 6 +
NodeFlow/Env/MsgControllerOfServer.cs | 16 +
NodeFlow/Env/RemoteFlowEnvironment.cs | 578 +++++++++++-------
NodeFlow/FlowFunc.cs | 54 +-
NodeFlow/Model/SingleConditionNode.cs | 5 +
NodeFlow/Model/SingleExpOpNode.cs | 5 +
NodeFlow/Model/SingleGlobalDataNode.cs | 56 +-
NodeFlow/Model/SingleScriptNode.cs | 7 +
Serein.Script/Serein.Script.csproj | 3 +
Serein.Script/TestExpression/Class1.cs | 70 +++
WorkBench/App.xaml.cs | 33 +-
WorkBench/LogWindow.xaml.cs | 14 +-
WorkBench/MainWindow.xaml.cs | 242 ++++----
WorkBench/Themes/MethodDetailsControl.xaml | 15 +-
Workbench/Node/INodeContainerControl.cs | 33 +
Workbench/Node/INodeJunction.cs | 3 +
Workbench/Node/View/GlobalDataControl.xaml.cs | 33 +-
.../GlobalDataNodeControlViewModel.cs | 16 +-
30 files changed, 979 insertions(+), 760 deletions(-)
create mode 100644 Library/Api/INodeContainer.cs
create mode 100644 Serein.Script/TestExpression/Class1.cs
create mode 100644 Workbench/Node/INodeContainerControl.cs
diff --git a/FlowStartTool/FlowEnv.cs b/FlowStartTool/FlowEnv.cs
index fed72d1..7186fa9 100644
--- a/FlowStartTool/FlowEnv.cs
+++ b/FlowStartTool/FlowEnv.cs
@@ -41,7 +41,7 @@ namespace Serein.FlowStartTool
// 获取环境输出
Env.OnEnvOut += (infoType, value) =>
{
- Console.WriteLine($"{DateTime.UtcNow} [{infoType}] : {value}{Environment.NewLine}");
+ Console.WriteLine($"{DateTime.Now} [{infoType}] : {value}{Environment.NewLine}");
};
await Env.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index 0ba3861..f185196 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -53,6 +53,12 @@ namespace Serein.Library.Api
///
public delegate void NodeCreateHandler(NodeCreateEventArgs eventArgs);
+ ///
+ /// 容器节点与子项节点的关系发生改变
+ ///
+ ///
+ public delegate void NodeContainerChildChangeHandler(NodeContainerChildChangeEventArgs eventArgs);
+
///
/// 环境中流程起始节点发生了改变
///
@@ -279,34 +285,80 @@ namespace Serein.Library.Api
///
/// 节点对象
/// 位置
- public NodeCreateEventArgs(object nodeModel, PositionOfUI position)
+ public NodeCreateEventArgs(NodeModelBase nodeModel, PositionOfUI position)
{
this.NodeModel = nodeModel;
this.Position = position;
}
- ///
- /// 区域子项节点添加事件参数
- ///
- /// 节点对象
- /// 是否添加在区域中
- /// 区域Guid
- public NodeCreateEventArgs(object nodeModel, bool isAddInRegion, string regeionGuid)
- {
- this.NodeModel = nodeModel;
- this.RegeionGuid = regeionGuid;
- this.IsAddInRegion = isAddInRegion;
- }
+ /////
+ ///// 区域子项节点添加事件参数
+ /////
+ ///// 节点对象
+ ///// 是否添加在区域中
+ ///// 区域Guid
+ //public NodeCreateEventArgs(object nodeModel, bool isAddInRegion, string regeionGuid)
+ //{
+ // this.NodeModel = nodeModel;
+ // this.RegeionGuid = regeionGuid;
+ // this.IsAddInRegion = isAddInRegion;
+ //}
///
/// 节点Model对象,目前需要手动转换对应的类型
///
public object NodeModel { get; private set; }
public PositionOfUI Position { get; private set; }
- public bool IsAddInRegion { get; private set; }
+ //public bool IsAddInRegion { get; private set; }
public string RegeionGuid { get; private set; }
}
+ ///
+ /// 节点父子关系改变
+ ///
+ public class NodeContainerChildChangeEventArgs : FlowEventArgs
+ {
+ ///
+ /// 变更类型
+ ///
+ public enum Type
+ {
+ ///
+ /// 放置
+ ///
+ Place,
+ ///
+ /// 取出
+ ///
+ TakeOut
+ }
+ ///
+ ///
+ ///
+ /// 子项节点
+ /// 容器节点
+ /// 类别
+ public NodeContainerChildChangeEventArgs(string childNodeGuid, string containerNodeGuid, Type state)
+ {
+ ChildNodeGuid = childNodeGuid;
+ ContainerNodeGuid = containerNodeGuid;
+ State = state;
+ }
+ ///
+ /// 子节点,该数据为此次时间的主节点
+ ///
+ public string ChildNodeGuid { get; private set; }
+ ///
+ /// 父节点
+ ///
+ public string ContainerNodeGuid { get; private set; }
+
+ ///
+ /// 改变类型
+ ///
+ public Type State { get; private set; }
+ }
+
///
/// 环境中移除了一个节点
///
@@ -611,6 +663,11 @@ namespace Serein.Library.Api
///
event NodeRemoveHandler OnNodeRemove;
+ ///
+ /// 节点父子关系发生改变事件
+ ///
+ event NodeContainerChildChangeHandler OnNodeParentChildChange;
+
///
/// 起始节点变化事件
///
@@ -647,7 +704,7 @@ namespace Serein.Library.Api
event NodeLocatedHandler OnNodeLocated;
///
- /// 节点移动了(远程插件)
+ /// 节点移动了(远程环境)
///
event NodeMovedHandler OnNodeMoved;
@@ -811,13 +868,22 @@ namespace Serein.Library.Api
Task LoadNodeInfosAsync(List nodeInfos);
///
- /// 创建节点/区域/基础控件
+ /// 创建节点
///
- /// 节点/区域/基础控件类型
+ /// 控件类型
/// 节点在画布上的位置(
/// 节点绑定的方法说明
Task CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
+ ///
+ /// 将节点放置在容器中/从容器中取出
+ ///
+ /// 子节点(主要节点)
+ /// 父节点
+ /// 是否组合(反之为分解节点组合关系)
+ ///
+ Task ChangeNodeContainerChild(string childNodeGuid,string parentNodeGuid,bool isAssembly);
+
///
/// 设置两个节点某个类型的方法调用关系为优先调用
///
diff --git a/Library/Api/INodeContainer.cs b/Library/Api/INodeContainer.cs
new file mode 100644
index 0000000..5473605
--- /dev/null
+++ b/Library/Api/INodeContainer.cs
@@ -0,0 +1,31 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Library.Api
+{
+ ///
+ /// 约束具有容器功能的节点应该有什么方法
+ ///
+ public interface INodeContainer
+ {
+ ///
+ /// 放置一个节点
+ ///
+ ///
+ void PlaceNode(NodeModelBase nodeModel);
+
+ ///
+ /// 取出一个节点
+ ///
+ ///
+ void TakeOutNode(NodeModelBase nodeModel);
+
+ ///
+ /// 取出所有节点(用于删除容器)
+ ///
+ void TakeOutAll();
+ }
+}
diff --git a/Library/FlowNode/NodeModelBaseData.cs b/Library/FlowNode/NodeModelBaseData.cs
index 99d0749..f7bb63a 100644
--- a/Library/FlowNode/NodeModelBaseData.cs
+++ b/Library/FlowNode/NodeModelBaseData.cs
@@ -12,7 +12,7 @@ namespace Serein.Library
///
- /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
+ /// 节点基类(数据)
///
[NodeProperty(ValuePath = NodeValuePath.None)]
public abstract partial class NodeModelBase : IDynamicFlowNode
@@ -69,6 +69,16 @@ namespace Serein.Library
public abstract partial class NodeModelBase : IDynamicFlowNode
{
+ ///
+ /// 是否为基础节点
+ ///
+ public virtual bool IsBase { get; } = false;
+
+ ///
+ /// 可以放置多少个节点
+ ///
+ public virtual int MaxChildrenCount { get; } = 0;
+
public NodeModelBase(IFlowEnvironment environment)
{
PreviousNodes = new Dictionary>();
@@ -78,21 +88,33 @@ namespace Serein.Library
PreviousNodes[ctType] = new List();
SuccessorNodes[ctType] = new List();
}
+ ChildrenNode = new List();
DebugSetting = new NodeDebugSetting(this);
this.Env = environment;
}
///
- /// 不同分支的父节点
+ /// 不同分支的父节点(流程调用)
///
public Dictionary> PreviousNodes { get; }
///
- /// 不同分支的子节点
+ /// 不同分支的子节点(流程调用)
///
public Dictionary> SuccessorNodes { get; }
+ ///
+ /// 该节点的父级节点(容器)
+ ///
+ public NodeModelBase ParentNode { get; set; } = null;
+
+ ///
+ /// 该节点的子项节点(容器)
+ ///
+ public List ChildrenNode { get; }
+
+
}
}
diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs
index ef01b3a..2b75bf3 100644
--- a/Library/FlowNode/NodeModelBaseFunc.cs
+++ b/Library/FlowNode/NodeModelBaseFunc.cs
@@ -164,6 +164,8 @@ namespace Serein.Library
IsProtectionParameter = this.MethodDetails.IsProtectionParameter,
IsInterrupt = this.DebugSetting.IsInterrupt,
IsEnable = this.DebugSetting.IsEnable,
+ ParentNodeGuid = ParentNode?.Guid,
+ ChildNodeGuids = ChildrenNode.Select(item => item.Guid).ToArray(),
};
nodeInfo.Position.X = Math.Round(nodeInfo.Position.X, 1);
nodeInfo.Position.Y = Math.Round(nodeInfo.Position.Y, 1);
diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs
index 33601cb..ac6f27d 100644
--- a/Library/FlowNode/ParameterDetails.cs
+++ b/Library/FlowNode/ParameterDetails.cs
@@ -180,7 +180,7 @@ namespace Serein.Library
public override string ToString()
{
- return $"[{this.Index}] {this.Name} : {this.ExplicitType.FullName} -> {this.DataType.FullName}";
+ return $"[{this.Index}] {this.Name} : {this.ExplicitType?.FullName} -> {this.DataType?.FullName}";
}
}
diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs
index ba3054c..4b7cd9b 100644
--- a/Library/FlowNode/SereinProjectData.cs
+++ b/Library/FlowNode/SereinProjectData.cs
@@ -230,8 +230,15 @@ namespace Serein.Library
///
public ParameterData[] ParameterData { get; set; }
+
///
- /// 如果是区域控件,则会存在子项。
+ /// 父级节点Guid
+ ///
+ public string ParentNodeGuid{ get; set; }
+
+
+ ///
+ /// 如果是区域控件,则会存在子项,这里记录的是子项的Guid。
///
public string[] ChildNodeGuids { get; set; }
diff --git a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs
index 56ff7e2..ce8679f 100644
--- a/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs
+++ b/Library/Network/WebSocket/Handle/WebSocketMsgHandleHelper.cs
@@ -173,10 +173,10 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
SereinEnv.WriteLine(InfoType.INFO, $"add websocket handle model :");
- SereinEnv.WriteLine(InfoType.ERROR, $"theme key, data key : {themeKey}, {dataKey}");
+ SereinEnv.WriteLine(InfoType.INFO, $"theme key, data key : {themeKey}, {dataKey}");
foreach (var config in configs)
{
- SereinEnv.WriteLine(InfoType.ERROR, $"theme value : {config.ThemeValue} ");
+ SereinEnv.WriteLine(InfoType.INFO, $"theme value : {config.ThemeValue} ");
var result = handleModule.AddHandleConfigs(config);
}
diff --git a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs
index a072463..af35c07 100644
--- a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs
+++ b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs
@@ -101,11 +101,11 @@ namespace Serein.Library.Utils.SereinExpression
{
result = InvokeMethod(targetObJ, operand);
}
- //else if (operation.Equals("@set",StringComparison.OrdinalIgnoreCase))
- //{
- // isChange = true;
- // result = SetMember(targetObJ, operand);
- //}
+ else if (operation.Equals("@set",StringComparison.OrdinalIgnoreCase))
+ {
+ isChange = true;
+ result = SetMember(targetObJ, operand);
+ }
else
{
throw new NotSupportedException($"Operation {operation} is not supported.");
diff --git a/NodeFlow/Env/EnvMsgTheme.cs b/NodeFlow/Env/EnvMsgTheme.cs
index c1f8afc..a243aa3 100644
--- a/NodeFlow/Env/EnvMsgTheme.cs
+++ b/NodeFlow/Env/EnvMsgTheme.cs
@@ -88,5 +88,10 @@
///
public const string SetMonitor = nameof(SetMonitor);
+ ///
+ /// 增加/减少方法可选类型的参数个数
+ ///
+ public const string ChangeParameter = nameof(ChangeParameter);
+
}
}
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index bcf8b38..c7eb290 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -9,6 +9,7 @@ using Serein.Library.Utils;
using Serein.Library.Utils.SereinExpression;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool;
+using Serein.Script.Node;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
@@ -150,6 +151,11 @@ namespace Serein.NodeFlow.Env
///
public event NodeRemoveHandler? OnNodeRemove;
+ ///
+ /// 节点父子关系发生改变事件
+ ///
+ public event NodeContainerChildChangeHandler OnNodeParentChildChange;
+
///
/// 起始节点变化事件
///
@@ -575,147 +581,7 @@ namespace Serein.NodeFlow.Env
LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
}
-#if false
- List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
- List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
- // 加载节点
- foreach (NodeInfo? nodeInfo in projectData.Nodes)
- {
- NodeControlType controlType = FlowFunc.GetNodeControlType(nodeInfo);
- if (controlType == NodeControlType.None)
- {
- continue;
- }
- MethodDetails? methodDetails = null;
-
- if (controlType.IsBaseNode())
- {
- // 加载基础节点
- methodDetails = new MethodDetails();
- }
- else
- {
- // 加载方法节点
- if (string.IsNullOrEmpty(nodeInfo.AssemblyName) && string.IsNullOrEmpty(nodeInfo.MethodName))
- {
- continue;
- }
- FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
- }
-
- var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
- nodeModel.LoadInfo(nodeInfo); // 创建节点model
- if (nodeModel is null)
- {
- nodeInfo.Guid = string.Empty;
- continue;
- }
-
- TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
- if (nodeInfo.ChildNodeGuids?.Length > 0)
- {
- regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids));
-
- UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)));
- }
- else
- {
- ordinaryNodes.Add((nodeModel, nodeInfo.Position));
- }
- }
-
-
-
- // 加载区域子项
- foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
- {
- foreach (var childNodeGuid in item.childNodeGuids)
- {
- NodeModels.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
- if (childNode is null)
- {
- // 节点尚未加载
- continue;
- }
- UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)));
- // 存在节点
-
- }
- }
- // 加载节点
- foreach ((NodeModelBase nodeModel, PositionOfUI position) item in ordinaryNodes)
- {
- bool IsContinue = false;
- foreach ((NodeModelBase region, string[] childNodeGuids) item2 in regionChildNodes)
- {
- foreach (var childNodeGuid in item2.childNodeGuids)
- {
- if (item.nodeModel.Guid.Equals(childNodeGuid))
- {
- IsContinue = true;
- }
- }
- }
- if (IsContinue) continue;
- UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
-
- }
-
-
-
- // 确定节点之间的连接关系
- Task.Run(async () =>
- {
- await Task.Delay(777);
- #region 方法调用关系
- foreach (var nodeInfo in projectData.Nodes)
- {
- if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
- {
- // 不存在对应的起始节点
- continue;
- }
- List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
- (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
- (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes),
- (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)];
-
- List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
- .Select(info => (info.connectionType,
- info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
- .ToArray()))
- .ToList();
- // 遍历每种类型的节点分支(四种)
- foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes)
- {
- // 遍历当前类型分支的节点(确认连接关系)
- foreach (var toNode in item.toNodes)
- {
- _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
- }
- }
- }
- #endregion
- #region 参数调用关系
- foreach (var toNode in NodeModels.Values)
- {
- for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++)
- {
- var pd = toNode.MethodDetails.ParameterDetailss[i];
- if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid)
- && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
- {
-
- await ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index);
- }
- }
- }
- #endregion
- });
-
- UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
-#endif
- LoadNodeInfosAsync(projectData.Nodes.ToList());
+ _ = LoadNodeInfosAsync(projectData.Nodes.ToList());
SetStartNode(projectData.StartNode);
}
@@ -899,6 +765,7 @@ namespace Serein.NodeFlow.Env
//}
}
+
///
/// 从节点信息集合批量加载节点控件
///
@@ -906,18 +773,17 @@ namespace Serein.NodeFlow.Env
///
public Task LoadNodeInfosAsync(List nodeInfos)
{
- List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
- List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
- // 加载节点
+ List needPlaceNodeInfos = [];
+ #region 从NodeInfo创建NodeModel
foreach (NodeInfo? nodeInfo in nodeInfos)
{
- NodeControlType controlType = FlowFunc.GetNodeControlType(nodeInfo);
- if (controlType == NodeControlType.None)
+ if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType))
{
continue;
}
- MethodDetails? methodDetails = null;
+ #region 获取方法描述
+ MethodDetails? methodDetails;
if (controlType.IsBaseNode())
{
// 加载基础节点
@@ -925,77 +791,50 @@ namespace Serein.NodeFlow.Env
}
else
{
+ if (string.IsNullOrEmpty(nodeInfo.MethodName)) continue;
// 加载方法节点
- if (string.IsNullOrEmpty(nodeInfo.MethodName))
- {
- continue;
- }
FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
- }
+ }
+ #endregion
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
- nodeModel.LoadInfo(nodeInfo); // 创建节点model
if (nodeModel is null)
{
nodeInfo.Guid = string.Empty;
continue;
}
-
+ nodeModel.LoadInfo(nodeInfo); // 创建节点model
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
- if (nodeInfo.ChildNodeGuids?.Length > 0)
+ if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
+ NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
{
- regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids));
-
- UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)));
- }
- else
- {
- ordinaryNodes.Add((nodeModel, nodeInfo.Position));
+ needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
}
+ UIContextOperation?.Invoke(() =>
+ OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上
}
+ #endregion
-
-
- // 加载区域子项
- foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
+ #region 重新放置节点
+ foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
{
- foreach (var childNodeGuid in item.childNodeGuids)
+ if (NodeModels.TryGetValue(nodeInfo.Guid, out var childNode) &&
+ NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
{
- NodeModels.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
- if (childNode is null)
- {
- // 节点尚未加载
- continue;
- }
- UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)));
- // 存在节点
+ childNode.ParentNode = parentNode;
+ parentNode.ChildrenNode.Add(childNode);
+ UIContextOperation?.Invoke(() => OnNodeParentChildChange?.Invoke(
+ new NodeContainerChildChangeEventArgs(childNode.Guid, parentNode.Guid,
+ NodeContainerChildChangeEventArgs.Type.Place)));
}
}
- // 加载节点
- foreach ((NodeModelBase nodeModel, PositionOfUI position) item in ordinaryNodes)
- {
- bool IsContinue = false;
- foreach ((NodeModelBase region, string[] childNodeGuids) item2 in regionChildNodes)
- {
- foreach (var childNodeGuid in item2.childNodeGuids)
- {
- if (item.nodeModel.Guid.Equals(childNodeGuid))
- {
- IsContinue = true;
- }
- }
- }
- if (IsContinue) continue;
- UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
+ #endregion
- }
-
- // 确定节点之间的连接关系
- Task.Run(async () =>
+ _ = Task.Run(async () =>
{
- await Task.Delay(400);
- #region 方法调用关系
+ await Task.Delay(100);
+ #region 确定节点之间的方法调用关系
foreach (var nodeInfo in nodeInfos)
{
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
@@ -1024,10 +863,11 @@ namespace Serein.NodeFlow.Env
}
}
#endregion
- #region 参数调用关系
+
+ #region 确定节点之间的参数调用关系
foreach (var toNode in NodeModels.Values)
{
- if(toNode.MethodDetails.ParameterDetailss == null)
+ if (toNode.MethodDetails.ParameterDetailss == null)
{
continue;
}
@@ -1038,13 +878,12 @@ namespace Serein.NodeFlow.Env
&& NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
{
- await ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index);
+ _ = ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index);
}
}
}
#endregion
});
-
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
return Task.CompletedTask;
@@ -1056,7 +895,9 @@ namespace Serein.NodeFlow.Env
///
///
/// 如果是表达式节点条件节点,该项为null
- public Task CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null)
+ public Task CreateNodeAsync(NodeControlType nodeControlType,
+ PositionOfUI position,
+ MethodDetailsInfo? methodDetailsInfo = null)
{
NodeModelBase? nodeModel;
@@ -1092,8 +933,40 @@ namespace Serein.NodeFlow.Env
}
var nodeInfo = nodeModel.ToInfo();
return Task.FromResult(nodeInfo);
-;
+ }
+ ///
+ /// 将节点放置在容器中/从容器中取出
+ ///
+ /// 子节点(主要节点)
+ /// 父节点
+ /// 是否组合(反之为分解节点组合关系)
+ ///
+ public async Task ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isPlace)
+ {
+ // 获取起始节点与目标节点
+ var childNode = GuidToModel(childNodeGuid);
+ var parentNode = GuidToModel(parentNodeGuid);
+ if (childNode is null || parentNode is null || parentNode is not INodeContainer nodeContainer) return false;
+
+ if (isPlace)
+ {
+ // 放置节点
+ parentNode.ChildrenNode.Add(childNode);
+ childNode.ParentNode = parentNode;
+ nodeContainer.PlaceNode(childNode);
+ }
+ else
+ {
+ // 取出节点
+ parentNode.ChildrenNode.Remove(childNode);
+ childNode.ParentNode = null;
+ nodeContainer.TakeOutNode(childNode);
+ }
+
+ OnNodeParentChildChange?.Invoke(new NodeContainerChildChangeEventArgs(childNodeGuid, parentNodeGuid,
+ isPlace ? NodeContainerChildChangeEventArgs.Type.Place : NodeContainerChildChangeEventArgs.Type.TakeOut));
+ return true;
}
///
@@ -1597,42 +1470,10 @@ namespace Serein.NodeFlow.Env
/// true,增加参数;false,减少参数
/// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)
///
- public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
+ public Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
{
var nodeModel = GuidToModel(nodeGuid);
- if (nodeModel is null) return false;
-
- var argInfo = nodeModel.MethodDetails.ParameterDetailss
- .Where(pd => !string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid))
- .Select(pd => (pd.ArgDataSourceNodeGuid, pd.ArgDataSourceType, pd.Index))
- .ToArray();
-
- #region 暂时移除连接关系
- foreach((var fromGuid, var type, var index) in argInfo)
- {
- await UIContextOperation.InvokeAsync(() =>
- OnNodeConnectChange?.Invoke(
- new NodeConnectChangeEventArgs(
- fromGuid, // 从哪个节点开始
- nodeModel.Guid, // 连接到那个节点
- JunctionOfConnectionType.Arg,
- index, // 参数
- type, // 连接线的样式类型
- NodeConnectChangeEventArgs.ConnectChangeType.Remote // 是创建连接还是删除连接
- ))); // 通知UI
- }
-
-
- for (int i = 0; i < argInfo.Length; i++)
- {
- ParameterDetails? pd = nodeModel.MethodDetails.ParameterDetailss[i];
- var fromNode = GuidToModel(pd.ArgDataSourceNodeGuid);
- if (fromNode is null) continue;
- var argSourceGuid = pd.ArgDataSourceNodeGuid;
- var argSourceType = pd.ArgDataSourceType;
- }
- #endregion
-
+ if (nodeModel is null) return Task.FromResult(false);
bool isPass;
if (isAdd)
{
@@ -1642,23 +1483,7 @@ namespace Serein.NodeFlow.Env
{
isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
}
-
- await Task.Delay(200);
- foreach ((var fromGuid, var type, var index) in argInfo)
- {
- await UIContextOperation.InvokeAsync(() =>
- OnNodeConnectChange?.Invoke(
- new NodeConnectChangeEventArgs(
- fromGuid, // 从哪个节点开始
- nodeModel.Guid, // 连接到那个节点
- JunctionOfConnectionType.Arg,
- index, // 参数
- type, // 连接线的样式类型
- NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
- ))); // 通知UI
-
- }
- return isPass;
+ return Task.FromResult(isPass);
}
diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs
index 47f63b4..5bb9237 100644
--- a/NodeFlow/Env/FlowEnvironmentDecorator.cs
+++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs
@@ -121,6 +121,15 @@ namespace Serein.NodeFlow.Env
remove { currentFlowEnvironment.OnNodeRemove -= value; }
}
+ ///
+ /// 节点父子关系发生改变事件
+ ///
+ public event NodeContainerChildChangeHandler OnNodeParentChildChange
+ {
+ add { currentFlowEnvironment.OnNodeParentChildChange += value; }
+ remove { currentFlowEnvironment.OnNodeParentChildChange -= value; }
+ }
+
public event StartNodeChangeHandler OnStartNodeChange
{
add { currentFlowEnvironment.OnStartNodeChange += value; }
@@ -278,6 +287,21 @@ namespace Serein.NodeFlow.Env
return result;
}
+ ///
+ /// 将节点放置在容器中/从容器中取出
+ ///
+ /// 子节点(主要节点)
+ /// 父节点
+ /// 是否组合(反之为分解节点组合关系)
+ ///
+ public async Task ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isAssembly)
+ {
+ SetProjectLoadingFlag(false);
+ var result = await currentFlowEnvironment.ChangeNodeContainerChild(childNodeGuid, parentNodeGuid, isAssembly); // 装饰器调用
+ SetProjectLoadingFlag(true);
+ return result;
+
+ }
diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs
index 1831e77..f236bcf 100644
--- a/NodeFlow/Env/MsgControllerOfClient.cs
+++ b/NodeFlow/Env/MsgControllerOfClient.cs
@@ -145,6 +145,12 @@ namespace Serein.NodeFlow.Env
{
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
}
+
+ [AutoSocketHandle(ThemeValue = EnvMsgTheme.ChangeParameter, IsReturnValue = false)]
+ public void ChangeParameter([UseMsgId] string msgId, bool state)
+ {
+ _ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
+ }
diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs
index 76191d8..3e20fb4 100644
--- a/NodeFlow/Env/MsgControllerOfServer.cs
+++ b/NodeFlow/Env/MsgControllerOfServer.cs
@@ -578,6 +578,22 @@ namespace Serein.NodeFlow.Env
await environment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
}
+ ///
+ /// 增加/减少节点入参可选类型参数的个数
+ ///
+ ///
+ ///
+ ///
+ [AutoSocketHandle(ThemeValue = EnvMsgTheme.ChangeParameter)]
+ public async Task