mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-05-01 12:59:30 +08:00
从节点Model解耦出容器接口,重新设计了节点的保存、加载。
This commit is contained in:
@@ -41,7 +41,7 @@ namespace Serein.FlowStartTool
|
|||||||
// 获取环境输出
|
// 获取环境输出
|
||||||
Env.OnEnvOut += (infoType, value) =>
|
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 监听远程请求
|
await Env.StartRemoteServerAsync(7525); // 启动 web socket 监听远程请求
|
||||||
|
|||||||
@@ -53,6 +53,12 @@ namespace Serein.Library.Api
|
|||||||
/// <param name="eventArgs"></param>
|
/// <param name="eventArgs"></param>
|
||||||
public delegate void NodeCreateHandler(NodeCreateEventArgs eventArgs);
|
public delegate void NodeCreateHandler(NodeCreateEventArgs eventArgs);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 容器节点与子项节点的关系发生改变
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventArgs"></param>
|
||||||
|
public delegate void NodeContainerChildChangeHandler(NodeContainerChildChangeEventArgs eventArgs);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 环境中流程起始节点发生了改变
|
/// 环境中流程起始节点发生了改变
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -279,34 +285,80 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeModel">节点对象</param>
|
/// <param name="nodeModel">节点对象</param>
|
||||||
/// <param name="position">位置</param>
|
/// <param name="position">位置</param>
|
||||||
public NodeCreateEventArgs(object nodeModel, PositionOfUI position)
|
public NodeCreateEventArgs(NodeModelBase nodeModel, PositionOfUI position)
|
||||||
{
|
{
|
||||||
this.NodeModel = nodeModel;
|
this.NodeModel = nodeModel;
|
||||||
this.Position = position;
|
this.Position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 区域子项节点添加事件参数
|
///// 区域子项节点添加事件参数
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <param name="nodeModel">节点对象</param>
|
///// <param name="nodeModel">节点对象</param>
|
||||||
/// <param name="isAddInRegion">是否添加在区域中</param>
|
///// <param name="isAddInRegion">是否添加在区域中</param>
|
||||||
/// <param name="regeionGuid">区域Guid</param>
|
///// <param name="regeionGuid">区域Guid</param>
|
||||||
public NodeCreateEventArgs(object nodeModel, bool isAddInRegion, string regeionGuid)
|
//public NodeCreateEventArgs(object nodeModel, bool isAddInRegion, string regeionGuid)
|
||||||
{
|
//{
|
||||||
this.NodeModel = nodeModel;
|
// this.NodeModel = nodeModel;
|
||||||
this.RegeionGuid = regeionGuid;
|
// this.RegeionGuid = regeionGuid;
|
||||||
this.IsAddInRegion = isAddInRegion;
|
// this.IsAddInRegion = isAddInRegion;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点Model对象,目前需要手动转换对应的类型
|
/// 节点Model对象,目前需要手动转换对应的类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object NodeModel { get; private set; }
|
public object NodeModel { get; private set; }
|
||||||
public PositionOfUI Position { 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 string RegeionGuid { get; private set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点父子关系改变
|
||||||
|
/// </summary>
|
||||||
|
public class NodeContainerChildChangeEventArgs : FlowEventArgs
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 变更类型
|
||||||
|
/// </summary>
|
||||||
|
public enum Type
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 放置
|
||||||
|
/// </summary>
|
||||||
|
Place,
|
||||||
|
/// <summary>
|
||||||
|
/// 取出
|
||||||
|
/// </summary>
|
||||||
|
TakeOut
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="childNodeGuid">子项节点</param>
|
||||||
|
/// <param name="containerNodeGuid">容器节点</param>
|
||||||
|
/// <param name="state">类别</param>
|
||||||
|
public NodeContainerChildChangeEventArgs(string childNodeGuid, string containerNodeGuid, Type state)
|
||||||
|
{
|
||||||
|
ChildNodeGuid = childNodeGuid;
|
||||||
|
ContainerNodeGuid = containerNodeGuid;
|
||||||
|
State = state;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 子节点,该数据为此次时间的主节点
|
||||||
|
/// </summary>
|
||||||
|
public string ChildNodeGuid { get; private set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 父节点
|
||||||
|
/// </summary>
|
||||||
|
public string ContainerNodeGuid { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 改变类型
|
||||||
|
/// </summary>
|
||||||
|
public Type State { get; private set; }
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 环境中移除了一个节点
|
/// 环境中移除了一个节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -611,6 +663,11 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
event NodeRemoveHandler OnNodeRemove;
|
event NodeRemoveHandler OnNodeRemove;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点父子关系发生改变事件
|
||||||
|
/// </summary>
|
||||||
|
event NodeContainerChildChangeHandler OnNodeParentChildChange;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 起始节点变化事件
|
/// 起始节点变化事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -647,7 +704,7 @@ namespace Serein.Library.Api
|
|||||||
event NodeLocatedHandler OnNodeLocated;
|
event NodeLocatedHandler OnNodeLocated;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点移动了(远程插件)
|
/// 节点移动了(远程环境)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
event NodeMovedHandler OnNodeMoved;
|
event NodeMovedHandler OnNodeMoved;
|
||||||
|
|
||||||
@@ -811,13 +868,22 @@ namespace Serein.Library.Api
|
|||||||
Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos);
|
Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建节点/区域/基础控件
|
/// 创建节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeType">节点/区域/基础控件类型</param>
|
/// <param name="nodeType">控件类型</param>
|
||||||
/// <param name="position">节点在画布上的位置(</param>
|
/// <param name="position">节点在画布上的位置(</param>
|
||||||
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
|
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
|
||||||
Task<NodeInfo> CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
|
Task<NodeInfo> CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将节点放置在容器中/从容器中取出
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||||
|
/// <param name="parentNodeGuid">父节点</param>
|
||||||
|
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> ChangeNodeContainerChild(string childNodeGuid,string parentNodeGuid,bool isAssembly);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置两个节点某个类型的方法调用关系为优先调用
|
/// 设置两个节点某个类型的方法调用关系为优先调用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
31
Library/Api/INodeContainer.cs
Normal file
31
Library/Api/INodeContainer.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 约束具有容器功能的节点应该有什么方法
|
||||||
|
/// </summary>
|
||||||
|
public interface INodeContainer
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 放置一个节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
void PlaceNode(NodeModelBase nodeModel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取出一个节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeModel"></param>
|
||||||
|
void TakeOutNode(NodeModelBase nodeModel);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取出所有节点(用于删除容器)
|
||||||
|
/// </summary>
|
||||||
|
void TakeOutAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ namespace Serein.Library
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
/// 节点基类(数据)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NodeProperty(ValuePath = NodeValuePath.None)]
|
[NodeProperty(ValuePath = NodeValuePath.None)]
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||||
@@ -69,6 +69,16 @@ namespace Serein.Library
|
|||||||
|
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为基础节点
|
||||||
|
/// </summary>
|
||||||
|
public virtual bool IsBase { get; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 可以放置多少个节点
|
||||||
|
/// </summary>
|
||||||
|
public virtual int MaxChildrenCount { get; } = 0;
|
||||||
|
|
||||||
public NodeModelBase(IFlowEnvironment environment)
|
public NodeModelBase(IFlowEnvironment environment)
|
||||||
{
|
{
|
||||||
PreviousNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
PreviousNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
||||||
@@ -78,21 +88,33 @@ namespace Serein.Library
|
|||||||
PreviousNodes[ctType] = new List<NodeModelBase>();
|
PreviousNodes[ctType] = new List<NodeModelBase>();
|
||||||
SuccessorNodes[ctType] = new List<NodeModelBase>();
|
SuccessorNodes[ctType] = new List<NodeModelBase>();
|
||||||
}
|
}
|
||||||
|
ChildrenNode = new List<NodeModelBase>();
|
||||||
DebugSetting = new NodeDebugSetting(this);
|
DebugSetting = new NodeDebugSetting(this);
|
||||||
this.Env = environment;
|
this.Env = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不同分支的父节点
|
/// 不同分支的父节点(流程调用)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> PreviousNodes { get; }
|
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> PreviousNodes { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不同分支的子节点
|
/// 不同分支的子节点(流程调用)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> SuccessorNodes { get; }
|
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> SuccessorNodes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 该节点的父级节点(容器)
|
||||||
|
/// </summary>
|
||||||
|
public NodeModelBase ParentNode { get; set; } = null;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 该节点的子项节点(容器)
|
||||||
|
/// </summary>
|
||||||
|
public List<NodeModelBase> ChildrenNode { get; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -164,6 +164,8 @@ namespace Serein.Library
|
|||||||
IsProtectionParameter = this.MethodDetails.IsProtectionParameter,
|
IsProtectionParameter = this.MethodDetails.IsProtectionParameter,
|
||||||
IsInterrupt = this.DebugSetting.IsInterrupt,
|
IsInterrupt = this.DebugSetting.IsInterrupt,
|
||||||
IsEnable = this.DebugSetting.IsEnable,
|
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.X = Math.Round(nodeInfo.Position.X, 1);
|
||||||
nodeInfo.Position.Y = Math.Round(nodeInfo.Position.Y, 1);
|
nodeInfo.Position.Y = Math.Round(nodeInfo.Position.Y, 1);
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ namespace Serein.Library
|
|||||||
|
|
||||||
public override string ToString()
|
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}";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -230,8 +230,15 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public ParameterData[] ParameterData { get; set; }
|
public ParameterData[] ParameterData { get; set; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 如果是区域控件,则会存在子项。
|
/// 父级节点Guid
|
||||||
|
/// </summary>
|
||||||
|
public string ParentNodeGuid{ get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 如果是区域控件,则会存在子项,这里记录的是子项的Guid。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string[] ChildNodeGuids { get; set; }
|
public string[] ChildNodeGuids { get; set; }
|
||||||
|
|
||||||
|
|||||||
@@ -173,10 +173,10 @@ namespace Serein.Library.Network.WebSocketCommunication.Handle
|
|||||||
|
|
||||||
|
|
||||||
SereinEnv.WriteLine(InfoType.INFO, $"add websocket handle model :");
|
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)
|
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);
|
var result = handleModule.AddHandleConfigs(config);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -101,11 +101,11 @@ namespace Serein.Library.Utils.SereinExpression
|
|||||||
{
|
{
|
||||||
result = InvokeMethod(targetObJ, operand);
|
result = InvokeMethod(targetObJ, operand);
|
||||||
}
|
}
|
||||||
//else if (operation.Equals("@set",StringComparison.OrdinalIgnoreCase))
|
else if (operation.Equals("@set",StringComparison.OrdinalIgnoreCase))
|
||||||
//{
|
{
|
||||||
// isChange = true;
|
isChange = true;
|
||||||
// result = SetMember(targetObJ, operand);
|
result = SetMember(targetObJ, operand);
|
||||||
//}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new NotSupportedException($"Operation {operation} is not supported.");
|
throw new NotSupportedException($"Operation {operation} is not supported.");
|
||||||
|
|||||||
@@ -88,5 +88,10 @@
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const string SetMonitor = nameof(SetMonitor);
|
public const string SetMonitor = nameof(SetMonitor);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加/减少方法可选类型的参数个数
|
||||||
|
/// </summary>
|
||||||
|
public const string ChangeParameter = nameof(ChangeParameter);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Serein.Library.Utils;
|
|||||||
using Serein.Library.Utils.SereinExpression;
|
using Serein.Library.Utils.SereinExpression;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
|
using Serein.Script.Node;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
@@ -150,6 +151,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public event NodeRemoveHandler? OnNodeRemove;
|
public event NodeRemoveHandler? OnNodeRemove;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点父子关系发生改变事件
|
||||||
|
/// </summary>
|
||||||
|
public event NodeContainerChildChangeHandler OnNodeParentChildChange;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 起始节点变化事件
|
/// 起始节点变化事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -575,147 +581,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
|
LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
|
||||||
}
|
}
|
||||||
|
|
||||||
#if false
|
_ = LoadNodeInfosAsync(projectData.Nodes.ToList());
|
||||||
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());
|
|
||||||
SetStartNode(projectData.StartNode);
|
SetStartNode(projectData.StartNode);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -899,6 +765,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
//}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从节点信息集合批量加载节点控件
|
/// 从节点信息集合批量加载节点控件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -906,18 +773,17 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
|
public Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
|
||||||
{
|
{
|
||||||
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
|
List<NodeInfo> needPlaceNodeInfos = [];
|
||||||
List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
|
#region 从NodeInfo创建NodeModel
|
||||||
// 加载节点
|
|
||||||
foreach (NodeInfo? nodeInfo in nodeInfos)
|
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||||
{
|
{
|
||||||
NodeControlType controlType = FlowFunc.GetNodeControlType(nodeInfo);
|
if (!EnumHelper.TryConvertEnum<NodeControlType>(nodeInfo.Type, out var controlType))
|
||||||
if (controlType == NodeControlType.None)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
MethodDetails? methodDetails = null;
|
|
||||||
|
|
||||||
|
#region 获取方法描述
|
||||||
|
MethodDetails? methodDetails;
|
||||||
if (controlType.IsBaseNode())
|
if (controlType.IsBaseNode())
|
||||||
{
|
{
|
||||||
// 加载基础节点
|
// 加载基础节点
|
||||||
@@ -925,77 +791,50 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (string.IsNullOrEmpty(nodeInfo.MethodName)) continue;
|
||||||
// 加载方法节点
|
// 加载方法节点
|
||||||
if (string.IsNullOrEmpty(nodeInfo.MethodName))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
|
FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||||
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
|
||||||
if (nodeModel is null)
|
if (nodeModel is null)
|
||||||
{
|
{
|
||||||
nodeInfo.Guid = string.Empty;
|
nodeInfo.Guid = string.Empty;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||||
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
|
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
|
||||||
if (nodeInfo.ChildNodeGuids?.Length > 0)
|
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||||
|
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||||
{
|
{
|
||||||
regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids));
|
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||||
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position)));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ordinaryNodes.Add((nodeModel, nodeInfo.Position));
|
|
||||||
}
|
}
|
||||||
|
UIContextOperation?.Invoke(() =>
|
||||||
|
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 重新放置节点
|
||||||
|
foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
|
||||||
// 加载区域子项
|
|
||||||
foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
|
|
||||||
{
|
{
|
||||||
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);
|
childNode.ParentNode = parentNode;
|
||||||
if (childNode is null)
|
parentNode.ChildrenNode.Add(childNode);
|
||||||
{
|
UIContextOperation?.Invoke(() => OnNodeParentChildChange?.Invoke(
|
||||||
// 节点尚未加载
|
new NodeContainerChildChangeEventArgs(childNode.Guid, parentNode.Guid,
|
||||||
continue;
|
NodeContainerChildChangeEventArgs.Type.Place)));
|
||||||
}
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid)));
|
|
||||||
// 存在节点
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 加载节点
|
#endregion
|
||||||
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 () =>
|
||||||
|
|
||||||
// 确定节点之间的连接关系
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
await Task.Delay(400);
|
await Task.Delay(100);
|
||||||
#region 方法调用关系
|
#region 确定节点之间的方法调用关系
|
||||||
foreach (var nodeInfo in nodeInfos)
|
foreach (var nodeInfo in nodeInfos)
|
||||||
{
|
{
|
||||||
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
||||||
@@ -1024,10 +863,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
#region 参数调用关系
|
|
||||||
|
#region 确定节点之间的参数调用关系
|
||||||
foreach (var toNode in NodeModels.Values)
|
foreach (var toNode in NodeModels.Values)
|
||||||
{
|
{
|
||||||
if(toNode.MethodDetails.ParameterDetailss == null)
|
if (toNode.MethodDetails.ParameterDetailss == null)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -1038,13 +878,12 @@ namespace Serein.NodeFlow.Env
|
|||||||
&& NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
|
&& NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode))
|
||||||
{
|
{
|
||||||
|
|
||||||
await ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index);
|
_ = ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
});
|
});
|
||||||
|
|
||||||
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
|
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
|
||||||
|
|
||||||
return Task.CompletedTask;
|
return Task.CompletedTask;
|
||||||
@@ -1056,7 +895,9 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="nodeControlType"></param>
|
/// <param name="nodeControlType"></param>
|
||||||
/// <param name="position"></param>
|
/// <param name="position"></param>
|
||||||
/// <param name="methodDetailsInfo">如果是表达式节点条件节点,该项为null</param>
|
/// <param name="methodDetailsInfo">如果是表达式节点条件节点,该项为null</param>
|
||||||
public Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null)
|
public Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType,
|
||||||
|
PositionOfUI position,
|
||||||
|
MethodDetailsInfo? methodDetailsInfo = null)
|
||||||
{
|
{
|
||||||
|
|
||||||
NodeModelBase? nodeModel;
|
NodeModelBase? nodeModel;
|
||||||
@@ -1092,8 +933,40 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
var nodeInfo = nodeModel.ToInfo();
|
var nodeInfo = nodeModel.ToInfo();
|
||||||
return Task.FromResult(nodeInfo);
|
return Task.FromResult(nodeInfo);
|
||||||
;
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将节点放置在容器中/从容器中取出
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||||
|
/// <param name="parentNodeGuid">父节点</param>
|
||||||
|
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1597,42 +1470,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||||
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
public Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||||
{
|
{
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
var nodeModel = GuidToModel(nodeGuid);
|
||||||
if (nodeModel is null) return false;
|
if (nodeModel is null) return Task.FromResult(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
|
|
||||||
|
|
||||||
bool isPass;
|
bool isPass;
|
||||||
if (isAdd)
|
if (isAdd)
|
||||||
{
|
{
|
||||||
@@ -1642,23 +1483,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
|
isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
|
||||||
}
|
}
|
||||||
|
return Task.FromResult(isPass);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,15 @@ namespace Serein.NodeFlow.Env
|
|||||||
remove { currentFlowEnvironment.OnNodeRemove -= value; }
|
remove { currentFlowEnvironment.OnNodeRemove -= value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点父子关系发生改变事件
|
||||||
|
/// </summary>
|
||||||
|
public event NodeContainerChildChangeHandler OnNodeParentChildChange
|
||||||
|
{
|
||||||
|
add { currentFlowEnvironment.OnNodeParentChildChange += value; }
|
||||||
|
remove { currentFlowEnvironment.OnNodeParentChildChange -= value; }
|
||||||
|
}
|
||||||
|
|
||||||
public event StartNodeChangeHandler OnStartNodeChange
|
public event StartNodeChangeHandler OnStartNodeChange
|
||||||
{
|
{
|
||||||
add { currentFlowEnvironment.OnStartNodeChange += value; }
|
add { currentFlowEnvironment.OnStartNodeChange += value; }
|
||||||
@@ -278,6 +287,21 @@ namespace Serein.NodeFlow.Env
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将节点放置在容器中/从容器中取出
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||||
|
/// <param name="parentNodeGuid">父节点</param>
|
||||||
|
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isAssembly)
|
||||||
|
{
|
||||||
|
SetProjectLoadingFlag(false);
|
||||||
|
var result = await currentFlowEnvironment.ChangeNodeContainerChild(childNodeGuid, parentNodeGuid, isAssembly); // 装饰器调用
|
||||||
|
SetProjectLoadingFlag(true);
|
||||||
|
return result;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -145,6 +145,12 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ChangeParameter, IsReturnValue = false)]
|
||||||
|
public void ChangeParameter([UseMsgId] string msgId, bool state)
|
||||||
|
{
|
||||||
|
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -578,6 +578,22 @@ namespace Serein.NodeFlow.Env
|
|||||||
await environment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
|
await environment.NotificationNodeValueChangeAsync(nodeGuid, path, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 增加/减少节点入参可选类型参数的个数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid"></param>
|
||||||
|
/// <param name="isAdd"></param>
|
||||||
|
/// <param name="paramIndex"></param>
|
||||||
|
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ChangeParameter)]
|
||||||
|
public async Task<object> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||||
|
{
|
||||||
|
var result = await environment.ChangeParameter(nodeGuid, isAdd, paramIndex);
|
||||||
|
return new
|
||||||
|
{
|
||||||
|
state = result
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,12 @@
|
|||||||
using Serein.Library;
|
using Newtonsoft.Json.Linq;
|
||||||
|
using Serein.Library;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
using Serein.Library.FlowNode;
|
using Serein.Library.FlowNode;
|
||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
|
using Serein.Script.Node;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.IO;
|
||||||
using System.Security.AccessControl;
|
using System.Security.AccessControl;
|
||||||
using System.Threading.Channels;
|
using System.Threading.Channels;
|
||||||
|
|
||||||
@@ -52,6 +55,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
public event NodeConnectChangeHandler OnNodeConnectChange;
|
public event NodeConnectChangeHandler OnNodeConnectChange;
|
||||||
public event NodeCreateHandler OnNodeCreate;
|
public event NodeCreateHandler OnNodeCreate;
|
||||||
public event NodeRemoveHandler OnNodeRemove;
|
public event NodeRemoveHandler OnNodeRemove;
|
||||||
|
/// <summary>
|
||||||
|
/// 节点父子关系发生改变事件
|
||||||
|
/// </summary>
|
||||||
|
public event NodeContainerChildChangeHandler OnNodeParentChildChange;
|
||||||
public event StartNodeChangeHandler OnStartNodeChange;
|
public event StartNodeChangeHandler OnStartNodeChange;
|
||||||
public event FlowRunCompleteHandler OnFlowRunComplete;
|
public event FlowRunCompleteHandler OnFlowRunComplete;
|
||||||
public event MonitorObjectChangeHandler OnMonitorObjectChange;
|
public event MonitorObjectChangeHandler OnMonitorObjectChange;
|
||||||
@@ -169,165 +176,179 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 加载节点数据,如果是区域控件,提前加载区域
|
|
||||||
var projectData = flowEnvInfo.Project;
|
_ = LoadNodeInfosAsync(flowEnvInfo.Project.Nodes.ToList());
|
||||||
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
|
SetStartNode(flowEnvInfo.Project.StartNode); // 设置流程起点
|
||||||
List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
|
|
||||||
// 加载节点
|
|
||||||
foreach (var nodeInfo in projectData.Nodes)
|
|
||||||
{
|
|
||||||
var controlType = FlowFunc.GetNodeControlType(nodeInfo);
|
|
||||||
if (controlType == NodeControlType.None)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
MethodDetails? methodDetails = null;
|
|
||||||
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
|
|
||||||
{
|
|
||||||
MethodDetailss.TryGetValue(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));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 加载区域中的节点
|
|
||||||
// 加载区域子项
|
|
||||||
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)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 加载普通的节点
|
|
||||||
// 加载节点
|
|
||||||
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;
|
|
||||||
//OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 确定节点之间的连接关系
|
|
||||||
_ = Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await Task.Delay(500);
|
|
||||||
#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)
|
|
||||||
{
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
|
||||||
toNode.Guid,
|
|
||||||
JunctionOfConnectionType.Invoke,
|
|
||||||
item.connectionType,
|
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 连接节点的传参关系
|
|
||||||
foreach (var toNode in NodeModels.Values)
|
|
||||||
{
|
|
||||||
if(toNode.MethodDetails.ParameterDetailss is null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
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))
|
|
||||||
{
|
|
||||||
UIContextOperation?.Invoke(() =>
|
|
||||||
OnNodeConnectChange?.Invoke(
|
|
||||||
new NodeConnectChangeEventArgs(
|
|
||||||
fromNode.Guid, // 从哪个节点开始
|
|
||||||
toNode.Guid, // 连接到那个节点
|
|
||||||
JunctionOfConnectionType.Arg,
|
|
||||||
(int)pd.Index, // 连接线的样式类型
|
|
||||||
pd.ArgDataSourceType,
|
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
|
||||||
))); // 通知UI
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
});
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
SetStartNode(projectData.StartNode); // 设置流程起点
|
|
||||||
UIContextOperation?.Invoke(() =>
|
UIContextOperation?.Invoke(() =>
|
||||||
{
|
{
|
||||||
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成
|
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成
|
||||||
});
|
});
|
||||||
IsLoadingProject = false;
|
IsLoadingProject = false;
|
||||||
|
|
||||||
|
#region 暂时注释
|
||||||
|
/* #region 加载节点数据,如果是区域控件,提前加载区域
|
||||||
|
var projectData = flowEnvInfo.Project;
|
||||||
|
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
|
||||||
|
List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
|
||||||
|
// 加载节点
|
||||||
|
foreach (var nodeInfo in projectData.Nodes)
|
||||||
|
{
|
||||||
|
var controlType = FlowFunc.GetNodeControlType(nodeInfo);
|
||||||
|
if (controlType == NodeControlType.None)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
MethodDetails? methodDetails = null;
|
||||||
|
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||||
|
{
|
||||||
|
MethodDetailss.TryGetValue(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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 加载区域中的节点
|
||||||
|
// 加载区域子项
|
||||||
|
//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)));
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 加载普通的节点
|
||||||
|
// 加载节点
|
||||||
|
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;
|
||||||
|
//OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
|
||||||
|
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position)));
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 确定节点之间的连接关系
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(500);
|
||||||
|
#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)
|
||||||
|
{
|
||||||
|
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||||
|
toNode.Guid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
|
item.connectionType,
|
||||||
|
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 连接节点的传参关系
|
||||||
|
foreach (var toNode in NodeModels.Values)
|
||||||
|
{
|
||||||
|
if(toNode.MethodDetails.ParameterDetailss is null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
UIContextOperation?.Invoke(() =>
|
||||||
|
OnNodeConnectChange?.Invoke(
|
||||||
|
new NodeConnectChangeEventArgs(
|
||||||
|
fromNode.Guid, // 从哪个节点开始
|
||||||
|
toNode.Guid, // 连接到那个节点
|
||||||
|
JunctionOfConnectionType.Arg,
|
||||||
|
(int)pd.Index, // 连接线的样式类型
|
||||||
|
pd.ArgDataSourceType,
|
||||||
|
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||||
|
))); // 通知UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
});
|
||||||
|
#endregion*/
|
||||||
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private bool TryAddNode(NodeModelBase nodeModel)
|
private bool TryAddNode(NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
//nodeModel.Guid ??= Guid.NewGuid().ToString();
|
//nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||||
@@ -345,69 +366,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType)
|
|
||||||
{
|
|
||||||
if (fromNode is null || toNode is null || fromNode == toNode)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ToExistOnFrom = true;
|
|
||||||
var FromExistInTo = true;
|
|
||||||
ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed,
|
|
||||||
ConnectionInvokeType.IsFail,
|
|
||||||
ConnectionInvokeType.IsError,
|
|
||||||
ConnectionInvokeType.Upstream];
|
|
||||||
|
|
||||||
|
|
||||||
foreach (ConnectionInvokeType ctType in ct)
|
|
||||||
{
|
|
||||||
var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray();
|
|
||||||
var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray();
|
|
||||||
ToExistOnFrom = FToTo.Length > 0;
|
|
||||||
FromExistInTo = ToOnF.Length > 0;
|
|
||||||
if (ToExistOnFrom && FromExistInTo)
|
|
||||||
{
|
|
||||||
this.WriteLine(InfoType.ERROR, "起始节点已与目标节点存在连接");
|
|
||||||
|
|
||||||
//return;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 检查是否可能存在异常
|
|
||||||
if (!ToExistOnFrom && FromExistInTo)
|
|
||||||
{
|
|
||||||
this.WriteLine(InfoType.ERROR, "目标节点不是起始节点的子节点,起始节点却是目标节点的父节点");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else if (ToExistOnFrom && !FromExistInTo)
|
|
||||||
{
|
|
||||||
//
|
|
||||||
this.WriteLine(InfoType.ERROR, " 起始节点不是目标节点的父节点,目标节点却是起始节点的子节点");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else // if (!ToExistOnFrom && !FromExistInTo)
|
|
||||||
{
|
|
||||||
// 可以正常连接
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支
|
|
||||||
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
|
|
||||||
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
|
||||||
toNode.Guid,
|
|
||||||
JunctionOfConnectionType.Invoke,
|
|
||||||
connectionType,
|
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<FlowEnvInfo> GetEnvInfoAsync()
|
public async Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||||
{
|
{
|
||||||
var envInfo = await msgClient.SendAndWaitDataAsync<FlowEnvInfo>(EnvMsgTheme.GetEnvInfo);
|
var envInfo = await msgClient.SendAndWaitDataAsync<FlowEnvInfo>(EnvMsgTheme.GetEnvInfo);
|
||||||
@@ -491,16 +450,16 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
nodeGuid
|
nodeGuid
|
||||||
});
|
});
|
||||||
//UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
||||||
{
|
{
|
||||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现接口 InvokeNodeAsync");
|
this.WriteLine(InfoType.INFO, "远程环境尚未实现接口 InvokeNodeAsync");
|
||||||
_ = msgClient.SendAsync(EnvMsgTheme.SetStartNode, new
|
//_ = msgClient.SendAsync(EnvMsgTheme.InvokeNodeAsync, new
|
||||||
{
|
//{
|
||||||
nodeGuid
|
// nodeGuid
|
||||||
});
|
//});
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -716,7 +675,133 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
|
public async Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
|
||||||
{
|
{
|
||||||
this.WriteLine(InfoType.WARN, "远程环境尚未实现的接口(重要,会尽快实现):LoadNodeInfoAsync");
|
List<NodeInfo> needPlaceNodeInfos = [];
|
||||||
|
|
||||||
|
#region 从NodeInfo创建NodeModel
|
||||||
|
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||||
|
{
|
||||||
|
if (!EnumHelper.TryConvertEnum<NodeControlType>(nodeInfo.Type, out var controlType))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 获取方法描述
|
||||||
|
MethodDetails? methodDetails = null;
|
||||||
|
if (string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||||
|
{
|
||||||
|
methodDetails = new MethodDetails();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载远程环境时尝试获取方法信息
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||||
|
if (nodeModel is null)
|
||||||
|
{
|
||||||
|
nodeInfo.Guid = string.Empty;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||||
|
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
|
||||||
|
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||||
|
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||||
|
{
|
||||||
|
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||||
|
}
|
||||||
|
UIContextOperation?.Invoke(() =>
|
||||||
|
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 重新放置节点
|
||||||
|
foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
|
||||||
|
{
|
||||||
|
if (NodeModels.TryGetValue(nodeInfo.Guid, out var childNode) &&
|
||||||
|
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||||
|
{
|
||||||
|
childNode.ParentNode = parentNode;
|
||||||
|
parentNode.ChildrenNode.Add(childNode);
|
||||||
|
UIContextOperation?.Invoke(() => OnNodeParentChildChange?.Invoke(
|
||||||
|
new NodeContainerChildChangeEventArgs(childNode.Guid, parentNode.Guid,
|
||||||
|
NodeContainerChildChangeEventArgs.Type.Place)));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(100);
|
||||||
|
#region 确定节点之间的方法调用关系
|
||||||
|
foreach (var nodeInfo in nodeInfos)
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||||
|
toNode.Guid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
|
item.connectionType,
|
||||||
|
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 确定节点之间的参数调用关系
|
||||||
|
foreach (var toNode in NodeModels.Values)
|
||||||
|
{
|
||||||
|
if (toNode.MethodDetails.ParameterDetailss == null)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
|
||||||
|
UIContextOperation?.Invoke(() =>
|
||||||
|
OnNodeConnectChange?.Invoke(
|
||||||
|
new NodeConnectChangeEventArgs(
|
||||||
|
fromNode.Guid, // 从哪个节点开始
|
||||||
|
toNode.Guid, // 连接到那个节点
|
||||||
|
JunctionOfConnectionType.Arg,
|
||||||
|
(int)pd.Index, // 连接线的样式类型
|
||||||
|
pd.ArgDataSourceType,
|
||||||
|
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||||
|
))); // 通知UI
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
});
|
||||||
|
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -756,6 +841,21 @@ namespace Serein.NodeFlow.Env
|
|||||||
});
|
});
|
||||||
return nodeInfo;
|
return nodeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将节点放置在容器中/从容器中取出
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||||
|
/// <param name="parentNodeGuid">父节点</param>
|
||||||
|
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isAssembly)
|
||||||
|
{
|
||||||
|
this.WriteLine(InfoType.WARN, "远程环境尚未实现的接口(重要,会尽快实现):ChangeNodeParentChild");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task<bool> RemoveNodeAsync(string nodeGuid)
|
public async Task<bool> RemoveNodeAsync(string nodeGuid)
|
||||||
{
|
{
|
||||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveNode, new
|
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveNode, new
|
||||||
@@ -875,7 +975,6 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
public void NodeLocated(string nodeGuid)
|
public void NodeLocated(string nodeGuid)
|
||||||
{
|
{
|
||||||
//Console.WriteLine("远程环境尚未实现的接口:NodeLocated");
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
|
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -886,8 +985,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.WriteLine(InfoType.INFO, $"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
|
//this.WriteLine(InfoType.INFO, $"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
|
||||||
|
|
||||||
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
|
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
|
||||||
{
|
{
|
||||||
nodeGuid = nodeGuid,
|
nodeGuid = nodeGuid,
|
||||||
@@ -906,8 +1004,32 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||||
{
|
{
|
||||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:ChangeParameter");
|
if (IsLoadingProject || IsLoadingNode)
|
||||||
return false;
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!NodeModels.TryGetValue(nodeGuid,out var nodeModel))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//this.WriteLine(InfoType.INFO, $"通知远程环境修改节点可选数据:{nodeGuid},isAdd:{isAdd},paramIndex:{paramIndex}");
|
||||||
|
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ChangeParameter, new
|
||||||
|
{
|
||||||
|
nodeGuid = nodeGuid,
|
||||||
|
isAdd = isAdd,
|
||||||
|
paramIndex = paramIndex,
|
||||||
|
});
|
||||||
|
if (result) {
|
||||||
|
if (isAdd)
|
||||||
|
{
|
||||||
|
nodeModel.MethodDetails.AddParamsArg(paramIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 流程依赖类库的接口
|
#region 流程依赖类库的接口
|
||||||
|
|||||||
@@ -71,36 +71,36 @@ namespace Serein.NodeFlow
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 从节点信息读取节点类型
|
///// 从节点信息读取节点类型
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <param name="nodeInfo"></param>
|
///// <param name="nodeInfo"></param>
|
||||||
/// <returns></returns>
|
///// <returns></returns>
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
///// <exception cref="NotImplementedException"></exception>
|
||||||
public static NodeControlType GetNodeControlType(NodeInfo nodeInfo)
|
//public static NodeControlType GetNodeControlType(NodeInfo nodeInfo)
|
||||||
{
|
//{
|
||||||
if(!EnumHelper.TryConvertEnum<NodeControlType>(nodeInfo.Type, out var controlType))
|
// if(!EnumHelper.TryConvertEnum<NodeControlType>(nodeInfo.Type, out var controlType))
|
||||||
{
|
// {
|
||||||
return NodeControlType.None;
|
// return NodeControlType.None;
|
||||||
}
|
// }
|
||||||
return controlType;
|
// return controlType;
|
||||||
// 创建控件实例
|
// // 创建控件实例
|
||||||
//NodeControlType controlType = nodeInfo.Type switch
|
// //NodeControlType controlType = nodeInfo.Type switch
|
||||||
//{
|
// //{
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件
|
||||||
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件
|
||||||
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
|
||||||
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleGlobalDataNode)}" => NodeControlType.GlobalData, // 数据节点
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleGlobalDataNode)}" => NodeControlType.GlobalData, // 数据节点
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleScriptNode)}" => NodeControlType.Script, // 数据节点
|
// // $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleScriptNode)}" => NodeControlType.Script, // 数据节点
|
||||||
// _ => NodeControlType.None,
|
// // _ => NodeControlType.None,
|
||||||
//};
|
// //};
|
||||||
//return controlType;
|
// //return controlType;
|
||||||
}
|
//}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 程序集封装依赖
|
/// 程序集封装依赖
|
||||||
|
|||||||
@@ -37,6 +37,11 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
public partial class SingleConditionNode : NodeModelBase
|
public partial class SingleConditionNode : NodeModelBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 条件表达式节点是基础节点
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsBase => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表达式参数索引
|
/// 表达式参数索引
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -26,6 +26,11 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
public partial class SingleExpOpNode : NodeModelBase
|
public partial class SingleExpOpNode : NodeModelBase
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 表达式节点是基础节点
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsBase => true;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表达式参数索引
|
/// 表达式参数索引
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -31,8 +31,17 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 全局数据节点
|
/// 全局数据节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SingleGlobalDataNode : NodeModelBase
|
public partial class SingleGlobalDataNode : NodeModelBase, INodeContainer
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 全局数据节点是基础节点
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsBase => true;
|
||||||
|
/// <summary>
|
||||||
|
/// 数据源只允许放置1个节点。
|
||||||
|
/// </summary>
|
||||||
|
public override int MaxChildrenCount => 1;
|
||||||
|
|
||||||
public SingleGlobalDataNode(IFlowEnvironment environment) : base(environment)
|
public SingleGlobalDataNode(IFlowEnvironment environment) : base(environment)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@@ -40,16 +49,33 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 数据来源的节点
|
/// 数据来源的节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private string? DataNodeGuid;
|
private NodeModelBase? DataNode;
|
||||||
|
|
||||||
|
|
||||||
|
public void PlaceNode(NodeModelBase nodeModel)
|
||||||
|
{
|
||||||
|
_ = this.Env.RemoveNodeAsync(DataNode?.Guid);
|
||||||
|
DataNode = nodeModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeOutAll()
|
||||||
|
{
|
||||||
|
DataNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeOutNode(NodeModelBase nodeModel)
|
||||||
|
{
|
||||||
|
DataNode = null;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置数据节点
|
/// 设置数据节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataNode"></param>
|
/// <param name="dataNode"></param>
|
||||||
public void SetDataNode(NodeModelBase dataNode)
|
//public void SetDataNode(NodeModelBase dataNode)
|
||||||
{
|
//{
|
||||||
DataNodeGuid = dataNode.Guid;
|
// DataNodeGuid = dataNode.Guid;
|
||||||
}
|
//}
|
||||||
|
|
||||||
private void ChangeName(string newName)
|
private void ChangeName(string newName)
|
||||||
{
|
{
|
||||||
@@ -73,7 +99,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据的KeyName不能为空[{this.Guid}]");
|
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据的KeyName不能为空[{this.Guid}]");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (DataNodeGuid == null)
|
if (DataNode is null)
|
||||||
{
|
{
|
||||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据节点没有设置数据来源[{this.Guid}]");
|
SereinEnv.WriteLine(InfoType.ERROR, $"全局数据节点没有设置数据来源[{this.Guid}]");
|
||||||
@@ -82,7 +108,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var result = await context.Env.InvokeNodeAsync(context, DataNodeGuid);
|
var result = await context.Env.InvokeNodeAsync(context, DataNode.Guid);
|
||||||
SereinEnv.AddOrUpdateFlowGlobalData(KeyName, result);
|
SereinEnv.AddOrUpdateFlowGlobalData(KeyName, result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -102,18 +128,9 @@ namespace Serein.NodeFlow.Model
|
|||||||
public override NodeInfo SaveCustomData(NodeInfo nodeInfo)
|
public override NodeInfo SaveCustomData(NodeInfo nodeInfo)
|
||||||
{
|
{
|
||||||
dynamic data = new ExpandoObject();
|
dynamic data = new ExpandoObject();
|
||||||
nodeInfo.CustomData = data;
|
|
||||||
|
|
||||||
data.KeyName = KeyName; // 变量名称
|
data.KeyName = KeyName; // 变量名称
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(DataNodeGuid))
|
nodeInfo.CustomData = data;
|
||||||
{
|
|
||||||
return nodeInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.DataNodeGuid = DataNodeGuid; // 数据节点Guid
|
|
||||||
|
|
||||||
nodeInfo.ChildNodeGuids = [DataNodeGuid];
|
|
||||||
return nodeInfo;
|
return nodeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,7 +141,6 @@ namespace Serein.NodeFlow.Model
|
|||||||
public override void LoadCustomData(NodeInfo nodeInfo)
|
public override void LoadCustomData(NodeInfo nodeInfo)
|
||||||
{
|
{
|
||||||
KeyName = nodeInfo.CustomData?.KeyName;
|
KeyName = nodeInfo.CustomData?.KeyName;
|
||||||
DataNodeGuid = nodeInfo.CustomData?.DataNodeGuid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -133,7 +149,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
public override void Remove()
|
public override void Remove()
|
||||||
{
|
{
|
||||||
// 移除数据节点
|
// 移除数据节点
|
||||||
_ = this.Env.RemoveNodeAsync(DataNodeGuid);
|
_ = this.Env.RemoveNodeAsync(DataNode?.Guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,13 @@ namespace Serein.NodeFlow.Model
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class SingleScriptNode : NodeModelBase
|
public partial class SingleScriptNode : NodeModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 脚本节点是基础节点
|
||||||
|
/// </summary>
|
||||||
|
public override bool IsBase => true;
|
||||||
|
|
||||||
|
|
||||||
private IScriptFlowApi ScriptFlowApi { get; }
|
private IScriptFlowApi ScriptFlowApi { get; }
|
||||||
|
|
||||||
private ASTNode mainNode;
|
private ASTNode mainNode;
|
||||||
|
|||||||
@@ -7,8 +7,11 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Remove="TestExpression\**" />
|
||||||
<Compile Remove="Tool\**" />
|
<Compile Remove="Tool\**" />
|
||||||
|
<EmbeddedResource Remove="TestExpression\**" />
|
||||||
<EmbeddedResource Remove="Tool\**" />
|
<EmbeddedResource Remove="Tool\**" />
|
||||||
|
<None Remove="TestExpression\**" />
|
||||||
<None Remove="Tool\**" />
|
<None Remove="Tool\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
|||||||
70
Serein.Script/TestExpression/Class1.cs
Normal file
70
Serein.Script/TestExpression/Class1.cs
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Script.TestExpression
|
||||||
|
{
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
public class ScriptParser
|
||||||
|
{
|
||||||
|
public static Expression<Func<T, bool>> ParseWhereExpression<T>(string lambdaText)
|
||||||
|
{
|
||||||
|
// 解析 lambda 表达式中的 item => item.StartsWith("张")
|
||||||
|
var match = Regex.Match(lambdaText, @"(?<param>\w+)\s*=>\s*(?<expression>.*)");
|
||||||
|
if (!match.Success) throw new Exception("Invalid lambda expression");
|
||||||
|
|
||||||
|
var paramName = match.Groups["param"].Value;
|
||||||
|
var expressionText = match.Groups["expression"].Value;
|
||||||
|
|
||||||
|
// 创建 Lambda 参数表达式
|
||||||
|
var param = Expression.Parameter(typeof(T), paramName);
|
||||||
|
|
||||||
|
// 构建 StartsWith("张") 的表达式
|
||||||
|
var startsWithMethod = typeof(string).GetMethod("StartsWith", new[] { typeof(string) });
|
||||||
|
var constantValue = Expression.Constant("张");
|
||||||
|
var methodCallExpression = Expression.Call(
|
||||||
|
Expression.Property(param, "StartsWith"),
|
||||||
|
startsWithMethod,
|
||||||
|
constantValue);
|
||||||
|
|
||||||
|
return Expression.Lambda<Func<T, bool>>(methodCallExpression, param);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Main()
|
||||||
|
{
|
||||||
|
// 假设你有一个List<string>作为数据源
|
||||||
|
var list = new List<string> { "张三", "李四", "张五" };
|
||||||
|
|
||||||
|
// 模拟从文本中解析出来的脚本
|
||||||
|
string script = "let list = GetList(); let newList = list.Where(item => item.StartsWith(\"张\")).ToList();";
|
||||||
|
|
||||||
|
// 解析Where表达式
|
||||||
|
var whereExpression = ParseWhereExpression<string>("item => item.StartsWith(\"张\")");
|
||||||
|
|
||||||
|
// 使用表达式执行LINQ查询
|
||||||
|
var filteredList = list.AsQueryable().Where(whereExpression).ToList();
|
||||||
|
|
||||||
|
foreach (var item in filteredList)
|
||||||
|
{
|
||||||
|
Console.WriteLine(item); // 输出: 张三, 张五
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class Class1
|
||||||
|
{
|
||||||
|
public Class1() {
|
||||||
|
List<string> list = new List<string>();
|
||||||
|
|
||||||
|
var newList = list.Where(item => item.StartsWith("张")).ToList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,17 +6,25 @@ using Serein.Library.Utils;
|
|||||||
using Serein.Library.Utils.SereinExpression;
|
using Serein.Library.Utils.SereinExpression;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using Serein.Script;
|
using Serein.Script;
|
||||||
|
using SqlSugar;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
|
using System.Reflection;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
using System.Windows.Threading;
|
using System.Windows.Threading;
|
||||||
|
using Expression = System.Linq.Expressions.Expression;
|
||||||
|
|
||||||
namespace Serein.Workbench
|
namespace Serein.Workbench
|
||||||
{
|
{
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
|
public class People
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
public int Age { get; set; }
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
@@ -28,11 +36,9 @@ namespace Serein.Workbench
|
|||||||
{
|
{
|
||||||
void LoadLocalProject()
|
void LoadLocalProject()
|
||||||
{
|
{
|
||||||
|
|
||||||
#if DEBUG
|
#if DEBUG
|
||||||
if (1 == 1)
|
if (1 == 11)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 这里是我自己的测试代码,你可以删除
|
// 这里是我自己的测试代码,你可以删除
|
||||||
string filePath;
|
string filePath;
|
||||||
filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\net8.0\PLCproject.dnf";
|
filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\net8.0\PLCproject.dnf";
|
||||||
@@ -42,7 +48,6 @@ namespace Serein.Workbench
|
|||||||
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||||
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
|
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
|
||||||
var dir = Path.GetDirectoryName(filePath);
|
var dir = Path.GetDirectoryName(filePath);
|
||||||
//System.IO.Directory.SetCurrentDirectory(dir);
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@@ -50,26 +55,8 @@ namespace Serein.Workbench
|
|||||||
public static SereinProjectData? FlowProjectData { get; set; }
|
public static SereinProjectData? FlowProjectData { get; set; }
|
||||||
public static string FileDataPath { get; set; } = "";
|
public static string FileDataPath { get; set; } = "";
|
||||||
|
|
||||||
public App()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnExit(ExitEventArgs e)
|
|
||||||
{
|
|
||||||
base.OnExit(e);
|
|
||||||
|
|
||||||
// 强制关闭所有窗口
|
|
||||||
foreach (Window window in Windows)
|
|
||||||
{
|
|
||||||
window.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private void Application_Startup(object sender, StartupEventArgs e)
|
private void Application_Startup(object sender, StartupEventArgs e)
|
||||||
{
|
{
|
||||||
Application.Current.Dispatcher.Invoke(() => { });
|
|
||||||
|
|
||||||
|
|
||||||
// 检查是否传入了参数
|
// 检查是否传入了参数
|
||||||
if (e.Args.Length == 1)
|
if (e.Args.Length == 1)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,11 +18,11 @@ namespace Serein.Workbench
|
|||||||
public partial class LogWindow : Window
|
public partial class LogWindow : Window
|
||||||
{
|
{
|
||||||
private StringBuilder logBuffer = new StringBuilder();
|
private StringBuilder logBuffer = new StringBuilder();
|
||||||
private int logUpdateInterval = 500; // 批量更新的时间间隔(毫秒)
|
private int logUpdateInterval = 200; // 批量更新的时间间隔(毫秒)
|
||||||
private Timer logUpdateTimer;
|
private Timer logUpdateTimer;
|
||||||
private const int MaxLines = 1000; // 最大显示的行数
|
private const int MaxLines = 1000; // 最大显示的行数
|
||||||
private bool autoScroll = true; // 自动滚动标识
|
private bool autoScroll = true; // 自动滚动标识
|
||||||
private int flushThreshold = 1000; // 设置日志刷新阈值
|
private int flushThreshold = 5; // 设置日志刷新阈值
|
||||||
private const int maxFlushSize = 1000; // 每次最大刷新字符数
|
private const int maxFlushSize = 1000; // 每次最大刷新字符数
|
||||||
|
|
||||||
public LogWindow()
|
public LogWindow()
|
||||||
@@ -49,12 +49,12 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
// 异步写入日志到文件
|
// 异步写入日志到文件
|
||||||
// Task.Run(() => File.AppendAllText("log.txt", text));
|
// Task.Run(() => File.AppendAllText("log.txt", text));
|
||||||
FlushLog();
|
//FlushLog();
|
||||||
// 如果日志达到阈值,立即刷新
|
// 如果日志达到阈值,立即刷新
|
||||||
//if (logBuffer.Length > flushThreshold)
|
if (logBuffer.Length > flushThreshold)
|
||||||
//{
|
{
|
||||||
// FlushLog();
|
FlushLog();
|
||||||
//}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -209,6 +209,7 @@ namespace Serein.Workbench
|
|||||||
EnvDecorator.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
|
EnvDecorator.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
|
||||||
EnvDecorator.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
|
EnvDecorator.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
|
||||||
EnvDecorator.OnNodeRemove += FlowEnvironment_NodeRemoteEvent;
|
EnvDecorator.OnNodeRemove += FlowEnvironment_NodeRemoteEvent;
|
||||||
|
EnvDecorator.OnNodeParentChildChange += EnvDecorator_OnNodeParentChildChange;
|
||||||
EnvDecorator.OnFlowRunComplete += FlowEnvironment_OnFlowRunComplete;
|
EnvDecorator.OnFlowRunComplete += FlowEnvironment_OnFlowRunComplete;
|
||||||
|
|
||||||
|
|
||||||
@@ -223,6 +224,8 @@ namespace Serein.Workbench
|
|||||||
EnvDecorator.OnEnvOut += FlowEnvironment_OnEnvOut;
|
EnvDecorator.OnEnvOut += FlowEnvironment_OnEnvOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 移除环境事件
|
/// 移除环境事件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -235,6 +238,7 @@ namespace Serein.Workbench
|
|||||||
EnvDecorator.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt;
|
EnvDecorator.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt;
|
||||||
EnvDecorator.OnNodeCreate -= FlowEnvironment_NodeCreateEvent;
|
EnvDecorator.OnNodeCreate -= FlowEnvironment_NodeCreateEvent;
|
||||||
EnvDecorator.OnNodeRemove -= FlowEnvironment_NodeRemoteEvent;
|
EnvDecorator.OnNodeRemove -= FlowEnvironment_NodeRemoteEvent;
|
||||||
|
EnvDecorator.OnNodeParentChildChange -= EnvDecorator_OnNodeParentChildChange;
|
||||||
EnvDecorator.OnFlowRunComplete -= FlowEnvironment_OnFlowRunComplete;
|
EnvDecorator.OnFlowRunComplete -= FlowEnvironment_OnFlowRunComplete;
|
||||||
|
|
||||||
|
|
||||||
@@ -313,7 +317,7 @@ namespace Serein.Workbench
|
|||||||
/// <param name="value"></param>
|
/// <param name="value"></param>
|
||||||
private void FlowEnvironment_OnEnvOut(InfoType type, string value)
|
private void FlowEnvironment_OnEnvOut(InfoType type, string value)
|
||||||
{
|
{
|
||||||
LogOutWindow.AppendText($"{DateTime.UtcNow} [{type}] : {value}{Environment.NewLine}");
|
LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -588,9 +592,14 @@ namespace Serein.Workbench
|
|||||||
_ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
|
_ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
|
||||||
};
|
};
|
||||||
|
|
||||||
if(IToJunction.ArgDataJunction.Length == 0)
|
if(IToJunction.ArgDataJunction.Length <= eventArgs.ArgIndex)
|
||||||
{
|
{
|
||||||
|
_ = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(1000);
|
||||||
|
FlowEnvironment_NodeConnectChangeEvemt(eventArgs);
|
||||||
|
});
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
|
JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
|
||||||
LineType lineType = LineType.Bezier;
|
LineType lineType = LineType.Bezier;
|
||||||
@@ -689,16 +698,11 @@ namespace Serein.Workbench
|
|||||||
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
|
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (eventArgs.NodeModel is not NodeModelBase nodeModelBase)
|
if (eventArgs.NodeModel is not NodeModelBase nodeModelBase)
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(nodeModelBase is null)
|
|
||||||
{
|
{
|
||||||
SereinEnv.WriteLine(InfoType.WARN, "OnNodeCreateEvent事件接收到意外的返回值");
|
SereinEnv.WriteLine(InfoType.WARN, "OnNodeCreateEvent事件接收到意外的返回值");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// MethodDetails methodDetailss = eventArgs.MethodDetailss;
|
|
||||||
PositionOfUI position = eventArgs.Position;
|
PositionOfUI position = eventArgs.Position;
|
||||||
|
|
||||||
if(!NodeMVVMManagement.TryGetType(nodeModelBase.ControlType, out var nodeMVVM))
|
if(!NodeMVVMManagement.TryGetType(nodeModelBase.ControlType, out var nodeMVVM))
|
||||||
@@ -713,36 +717,25 @@ namespace Serein.Workbench
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
NodeControlBase nodeControl = CreateNodeControl(nodeMVVM.ControlType, nodeMVVM.ViewModelType, nodeModelBase);
|
NodeControlBase nodeControl = CreateNodeControl(nodeMVVM.ControlType, nodeMVVM.ViewModelType, nodeModelBase); // 创建控件
|
||||||
|
|
||||||
if (nodeControl is null)
|
if (nodeControl is null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
NodeControls.TryAdd(nodeModelBase.Guid, nodeControl);
|
|
||||||
if (eventArgs.IsAddInRegion && NodeControls.TryGetValue(eventArgs.RegeionGuid, out NodeControlBase? regionControl))
|
NodeControls.TryAdd(nodeModelBase.Guid, nodeControl); // 添加到
|
||||||
|
if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器
|
||||||
{
|
{
|
||||||
// 这里的条件是用于加载项目文件时,直接加载在区域中,而不用再判断控件
|
// 通知运行环境调用加载节点子项的方法
|
||||||
if (regionControl is not null)
|
_ = EnvDecorator.ChangeNodeContainerChild(nodeControl.ViewModel.NodeModel.Guid,
|
||||||
{
|
regionControl.ViewModel.NodeModel.Guid,
|
||||||
TryPlaceNodeInRegion(regionControl, nodeControl);
|
true);
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 这里是正常的编辑流程
|
// 并非添加在容器中,直接放置节点
|
||||||
// 判断是否为区域
|
PlaceNodeOnCanvas(nodeControl, position.X, position.Y);
|
||||||
if (TryPlaceNodeInRegion(nodeControl, position, out var targetNodeControl))
|
|
||||||
{
|
|
||||||
// 需要将节点放置在区域中
|
|
||||||
TryPlaceNodeInRegion(targetNodeControl, nodeControl);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 并非区域,需要手动添加
|
|
||||||
PlaceNodeOnCanvas(nodeControl, position.X, position.Y); // 将节点放置在画布上
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -761,6 +754,38 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点父子关系发生改变
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="eventArgs"></param>
|
||||||
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
|
private void EnvDecorator_OnNodeParentChildChange(NodeContainerChildChangeEventArgs eventArgs)
|
||||||
|
{
|
||||||
|
string childNodeGuid = eventArgs.ChildNodeGuid;
|
||||||
|
string containerNodeGuid = eventArgs.ContainerNodeGuid;
|
||||||
|
if (!TryGetControl(childNodeGuid, out var childNodeControl)
|
||||||
|
|| !TryGetControl(containerNodeGuid, out var containerNodeControl))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(containerNodeControl is not INodeContainerControl containerControl)
|
||||||
|
{
|
||||||
|
SereinEnv.WriteLine(InfoType.WARN, $"节点[{childNodeGuid}]无法放置在节点[{containerNodeGuid}],因为后者并不实现 INodeContainerControl 接口");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventArgs.State == NodeContainerChildChangeEventArgs.Type.Place)
|
||||||
|
{
|
||||||
|
FlowChartCanvas.Children.Remove(childNodeControl);
|
||||||
|
containerControl.PlaceNode(childNodeControl); // 放置
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
containerControl.TakeOutNode(childNodeControl); // 取出
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 设置了流程起始控件
|
/// 设置了流程起始控件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1031,64 +1056,6 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 加载项目文件后触发事件相关方法
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行环节加载了项目文件,需要创建节点控件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeInfo"></param>
|
|
||||||
/// <param name="methodDetailss"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
|
||||||
//private NodeControlBase? CreateNodeControlOfNodeInfo(NodeInfo nodeInfo, MethodDetails methodDetailss)
|
|
||||||
//{
|
|
||||||
// // 创建控件实例
|
|
||||||
// NodeControlBase nodeControl = nodeInfo.Type switch
|
|
||||||
// {
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" =>
|
|
||||||
// CreateNodeControl<SingleActionNode, ActionNodeControl, ActionNodeControlViewModel>(methodDetailss),// 动作节点控件
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" =>
|
|
||||||
// CreateNodeControl<SingleFlipflopNode, FlipflopNodeControl, FlipflopNodeControlViewModel>(methodDetailss), // 触发器节点控件
|
|
||||||
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" =>
|
|
||||||
// CreateNodeControl<SingleConditionNode, ConditionNodeControl, ConditionNodeControlViewModel>(), // 条件表达式控件
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" =>
|
|
||||||
// CreateNodeControl<SingleExpOpNode, ExpOpNodeControl, ExpOpNodeViewModel>(), // 操作表达式控件
|
|
||||||
|
|
||||||
// $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" =>
|
|
||||||
// CreateNodeControl<CompositeConditionNode, ConditionRegionControl, ConditionRegionNodeControlViewModel>(), // 条件区域控件
|
|
||||||
// _ => throw new NotImplementedException($"非预期的节点类型{nodeInfo.Type}"),
|
|
||||||
// };
|
|
||||||
// return nodeControl;
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 加载文件时,添加节点到区域中
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="regionControl"></param>
|
|
||||||
/// <param name="childNodes"></param>
|
|
||||||
//private void AddNodeControlInRegeionControl(NodeControlBase regionControl, NodeInfo[] childNodes)
|
|
||||||
//{
|
|
||||||
// foreach (var childNode in childNodes)
|
|
||||||
// {
|
|
||||||
// if (FlowEnvironment.TryGetMethodDetails(childNode.MethodName, out MethodDetails md))
|
|
||||||
// {
|
|
||||||
// var childNodeControl = CreateNodeControlOfNodeInfo(childNode, md);
|
|
||||||
// if (childNodeControl is null)
|
|
||||||
// {
|
|
||||||
// Console.WriteLine($"无法为节点类型创建节点控件: {childNode.MethodName}\r\n");
|
|
||||||
// continue;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// if (regionControl is ConditionRegionControl conditionRegion)
|
|
||||||
// {
|
|
||||||
// conditionRegion.AddCondition(childNodeControl);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 节点控件的创建
|
#region 节点控件的创建
|
||||||
|
|
||||||
@@ -1370,9 +1337,13 @@ namespace Serein.Workbench
|
|||||||
{
|
{
|
||||||
if (sender is UserControl control)
|
if (sender is UserControl control)
|
||||||
{
|
{
|
||||||
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
|
if(e.LeftButton == MouseButtonState.Pressed)
|
||||||
var dragData = new DataObject(MouseNodeType.CreateBaseNodeInCanvas, control.GetType());
|
{
|
||||||
DragDrop.DoDragDrop(control, dragData, DragDropEffects.Move);
|
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
|
||||||
|
var dragData = new DataObject(MouseNodeType.CreateBaseNodeInCanvas, control.GetType());
|
||||||
|
DragDrop.DoDragDrop(control, dragData, DragDropEffects.Move);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1434,7 +1405,9 @@ namespace Serein.Workbench
|
|||||||
/// <param name="position"></param>
|
/// <param name="position"></param>
|
||||||
/// <param name="targetNodeControl">目标节点控件</param>
|
/// <param name="targetNodeControl">目标节点控件</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private bool TryPlaceNodeInRegion(NodeControlBase nodeControl, PositionOfUI position, out NodeControlBase targetNodeControl)
|
private bool TryPlaceNodeInRegion(NodeControlBase nodeControl,
|
||||||
|
PositionOfUI position,
|
||||||
|
out NodeControlBase targetNodeControl)
|
||||||
{
|
{
|
||||||
var point = new Point(position.X, position.Y);
|
var point = new Point(position.X, position.Y);
|
||||||
HitTestResult hitTestResult = VisualTreeHelper.HitTest(FlowChartCanvas, point);
|
HitTestResult hitTestResult = VisualTreeHelper.HitTest(FlowChartCanvas, point);
|
||||||
@@ -1452,9 +1425,10 @@ namespace Serein.Workbench
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 准备放置全局数据控件
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// 准备放置全局数据控件
|
||||||
GlobalDataControl? globalDataControl = GetParentOfType<GlobalDataControl>(hitElement);
|
GlobalDataControl? globalDataControl = GetParentOfType<GlobalDataControl>(hitElement);
|
||||||
if (globalDataControl is not null)
|
if (globalDataControl is not null)
|
||||||
{
|
{
|
||||||
@@ -1467,31 +1441,32 @@ namespace Serein.Workbench
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
///// <summary>
|
||||||
/// 将节点放在目标区域中
|
///// 将节点放在目标区域中
|
||||||
/// </summary>
|
///// </summary>
|
||||||
/// <param name="regionControl">区域容器</param>
|
///// <param name="regionControl">区域容器</param>
|
||||||
/// <param name="nodeControl">节点控件</param>
|
///// <param name="nodeControl">节点控件</param>
|
||||||
private void TryPlaceNodeInRegion(NodeControlBase regionControl, NodeControlBase nodeControl)
|
//private void TryPlaceNodeInRegion(NodeControlBase regionControl, NodeControlBase nodeControl)
|
||||||
{
|
//{
|
||||||
// 准备放置条件表达式控件
|
// // 准备放置条件表达式控件
|
||||||
if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition)
|
// if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition)
|
||||||
{
|
// {
|
||||||
if (regionControl is ConditionRegionControl conditionRegion)
|
// if (regionControl is ConditionRegionControl conditionRegion)
|
||||||
{
|
// {
|
||||||
conditionRegion.AddCondition(nodeControl); // 条件区域容器
|
// conditionRegion.AddCondition(nodeControl); // 条件区域容器
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
else if(regionControl.ViewModel.NodeModel.ControlType == NodeControlType.GlobalData)
|
// else if(regionControl.ViewModel.NodeModel.ControlType == NodeControlType.GlobalData)
|
||||||
{
|
// {
|
||||||
if (regionControl is GlobalDataControl globalDataControl)
|
// if (regionControl is GlobalDataControl globalDataControl)
|
||||||
{
|
// {
|
||||||
// 全局数据节点容器
|
// // 全局数据节点容器
|
||||||
globalDataControl.SetDataNodeControl(nodeControl);
|
// globalDataControl.SetDataNodeControl(nodeControl);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
|
/// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
|
||||||
@@ -2413,8 +2388,6 @@ namespace Serein.Workbench
|
|||||||
model.Guid = Guid.NewGuid().ToString();
|
model.Guid = Guid.NewGuid().ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert.ChangeType(model, targetType);
|
|
||||||
|
|
||||||
var viewModel = Activator.CreateInstance(viewModelType, [model]);
|
var viewModel = Activator.CreateInstance(viewModelType, [model]);
|
||||||
var controlObj = Activator.CreateInstance(controlType, [viewModel]);
|
var controlObj = Activator.CreateInstance(controlType, [viewModel]);
|
||||||
if (controlObj is NodeControlBase nodeControl)
|
if (controlObj is NodeControlBase nodeControl)
|
||||||
@@ -2516,9 +2489,8 @@ namespace Serein.Workbench
|
|||||||
// 获取主线程的 SynchronizationContext
|
// 获取主线程的 SynchronizationContext
|
||||||
Action<SynchronizationContext, Action> uiInvoke = (uiContext, action) => uiContext?.Post(state => action?.Invoke(), null);
|
Action<SynchronizationContext, Action> uiInvoke = (uiContext, action) => uiContext?.Post(state => action?.Invoke(), null);
|
||||||
|
|
||||||
|
SereinEnv.WriteLine(InfoType.INFO, "流程开始运行");
|
||||||
|
_ = Task.Run(async () =>
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
{
|
||||||
await EnvDecorator.StartAsync();
|
await EnvDecorator.StartAsync();
|
||||||
});
|
});
|
||||||
@@ -2752,32 +2724,34 @@ namespace Serein.Workbench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void CpoyNodeInfo()
|
private void CpoyNodeInfo()
|
||||||
{
|
{
|
||||||
|
if(selectNodeControls.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
// 处理复制操作
|
// 处理复制操作
|
||||||
var dictSelection = selectNodeControls
|
var dictSelection = selectNodeControls
|
||||||
.Select(control => control.ViewModel.NodeModel.ToInfo())
|
.Select(control => control.ViewModel.NodeModel).ToList();
|
||||||
.ToDictionary(kvp => kvp.Guid, kvp => kvp);
|
|
||||||
|
|
||||||
// 遍历当前已选节点
|
// 遍历当前已选节点
|
||||||
foreach (var node in dictSelection.Values.ToArray())
|
foreach (var node in dictSelection.ToArray())
|
||||||
{
|
{
|
||||||
if(node.ChildNodeGuids is null)
|
if(node.ChildrenNode.Count == 0)
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
// 遍历这些节点的子节点,获得完整的已选节点信息
|
// 遍历这些节点的子节点,添加过来
|
||||||
foreach (var childNodeGuid in node.ChildNodeGuids)
|
foreach (var childNode in node.ChildrenNode)
|
||||||
{
|
{
|
||||||
if(!dictSelection.ContainsKey(childNodeGuid) && NodeControls.TryGetValue(childNodeGuid,out var childNode))
|
dictSelection.Add(childNode);
|
||||||
{
|
|
||||||
dictSelection.Add(childNodeGuid, childNode.ViewModel.NodeModel.ToInfo());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var nodeInfos = dictSelection.Select(item => item.ToInfo());
|
||||||
|
|
||||||
JObject json = new JObject()
|
JObject json = new JObject()
|
||||||
{
|
{
|
||||||
["nodes"] = JArray.FromObject(dictSelection.Values)
|
["nodes"] = JArray.FromObject(nodeInfos)
|
||||||
};
|
};
|
||||||
|
|
||||||
var jsonText = json.ToString();
|
var jsonText = json.ToString();
|
||||||
|
|||||||
@@ -37,14 +37,13 @@
|
|||||||
<!--入参参数名称-->
|
<!--入参参数名称-->
|
||||||
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" Margin="2,0,2,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" Margin="2,0,2,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
<!--增加可选参数(如果有)-->
|
<!--增加可选参数(如果有)-->
|
||||||
<view:ParamsArgControl
|
<view:ParamsArgControl x:Name="ParamsArgControl"
|
||||||
x:Name="ParamsArgControl"
|
ArgIndex="{Binding Index}"
|
||||||
ArgIndex="{Binding Index}"
|
MyNode="{Binding NodeModel}"
|
||||||
MyNode="{Binding NodeModel}"
|
Width="12"
|
||||||
Width="12"
|
Grid.Column="5" Margin="2,0,2,0" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
Grid.Column="5" Margin="2,0,2,0" HorizontalAlignment="Right" VerticalAlignment="Center"
|
Visibility="{Binding IsParams, Mode=OneWay,
|
||||||
Visibility="{Binding IsParams, Mode=OneWay,
|
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}"
|
||||||
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ContentControl Content="{Binding}" Grid.Column="4" VerticalAlignment="Center">
|
<ContentControl Content="{Binding}" Grid.Column="4" VerticalAlignment="Center">
|
||||||
|
|||||||
33
Workbench/Node/INodeContainerControl.cs
Normal file
33
Workbench/Node/INodeContainerControl.cs
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
using Serein.Workbench.Node.View;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 约束具有容器功能的节点控件应该有什么方法
|
||||||
|
/// </summary>
|
||||||
|
public interface INodeContainerControl
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 放置一个节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeControl"></param>
|
||||||
|
void PlaceNode(NodeControlBase nodeControl);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取出一个节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeControl"></param>
|
||||||
|
void TakeOutNode(NodeControlBase nodeControl);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 取出所有节点(用于删除容器)
|
||||||
|
/// </summary>
|
||||||
|
void TakeOutAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ using System.Windows;
|
|||||||
|
|
||||||
namespace Serein.Workbench.Node
|
namespace Serein.Workbench.Node
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 约束一个节点应该有哪些控制点
|
/// 约束一个节点应该有哪些控制点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -20,10 +20,8 @@ namespace Serein.Workbench.Node.View
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// UserControl1.xaml 的交互逻辑
|
/// UserControl1.xaml 的交互逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class GlobalDataControl : NodeControlBase, INodeJunction
|
public partial class GlobalDataControl : NodeControlBase, INodeJunction, INodeContainerControl
|
||||||
{
|
{
|
||||||
//private new GlobalDataNodeControlViewModel ViewModel => ViewModel;
|
|
||||||
|
|
||||||
public GlobalDataControl() : base()
|
public GlobalDataControl() : base()
|
||||||
{
|
{
|
||||||
// 窗体初始化需要
|
// 窗体初始化需要
|
||||||
@@ -39,19 +37,6 @@ namespace Serein.Workbench.Node.View
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置数据节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="nodeControl"></param>
|
|
||||||
public void SetDataNodeControl(NodeControlBase nodeControl)
|
|
||||||
{
|
|
||||||
((GlobalDataNodeControlViewModel)ViewModel).SetDataNode(nodeControl.ViewModel.NodeModel);
|
|
||||||
|
|
||||||
GlobalDataPanel.Children.Clear();
|
|
||||||
GlobalDataPanel.Children.Add(nodeControl);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 入参控制点(可能有,可能没)
|
/// 入参控制点(可能有,可能没)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -73,5 +58,21 @@ namespace Serein.Workbench.Node.View
|
|||||||
JunctionControlBase[] INodeJunction.ArgDataJunction => throw new NotImplementedException();
|
JunctionControlBase[] INodeJunction.ArgDataJunction => throw new NotImplementedException();
|
||||||
|
|
||||||
|
|
||||||
|
public void PlaceNode(NodeControlBase nodeControl)
|
||||||
|
{
|
||||||
|
GlobalDataPanel.Children.Clear();
|
||||||
|
GlobalDataPanel.Children.Add(nodeControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeOutNode(NodeControlBase nodeControl)
|
||||||
|
{
|
||||||
|
GlobalDataPanel.Children.Remove(nodeControl);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void TakeOutAll()
|
||||||
|
{
|
||||||
|
GlobalDataPanel.Children.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
|
using Serein.Workbench.Node.View;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
@@ -44,18 +45,7 @@ namespace Serein.Workbench.Node.ViewModel
|
|||||||
set { NodeModel.KeyName = value; OnPropertyChanged(); }
|
set { NodeModel.KeyName = value; OnPropertyChanged(); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 设置数据节点
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="dataNode"></param>
|
|
||||||
public void SetDataNode(NodeModelBase dataNode)
|
|
||||||
{
|
|
||||||
NodeModel.SetDataNode(dataNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user