mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-05 15:56:33 +08:00
1. 重新设计了Generate项目及相关特性的命名,避免与其他类型混淆。
2. 补充了部分注释。 3. 修改了删除容器节点时,容器内子节点未正确删除的问题。
This commit is contained in:
633
NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs
Normal file
633
NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs
Normal file
@@ -0,0 +1,633 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Env;
|
||||
using Serein.NodeFlow.Model;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reactive;
|
||||
using System.Security.AccessControl;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using static Serein.Library.Api.NodeConnectChangeEventArgs;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点连接状态发生改变
|
||||
/// </summary>
|
||||
internal class ChangeNodeConnectionOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(ChangeNodeConnectionOperation);
|
||||
|
||||
/// <summary>
|
||||
/// 所在画布
|
||||
/// </summary>
|
||||
public required string CanvasGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接关系中始节点的Guid
|
||||
/// </summary>
|
||||
public required string FromNodeGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接关系中目标节点的Guid
|
||||
/// </summary>
|
||||
public required string ToNodeGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点连接控制点类型
|
||||
/// </summary>
|
||||
public JunctionType FromNodeJunctionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 目标节点连接控制点类型
|
||||
/// </summary>
|
||||
public JunctionType ToNodeJunctionType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接类型
|
||||
/// </summary>
|
||||
public ConnectionInvokeType ConnectionInvokeType { get; set; }
|
||||
/// <summary>
|
||||
/// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
|
||||
/// </summary>
|
||||
public ConnectChangeType ChangeType { get; set; }
|
||||
/// <summary>
|
||||
/// 指示需要创建什么类型的连接线
|
||||
/// </summary>
|
||||
public JunctionOfConnectionType JunctionOfConnectionType { get; set; } = JunctionOfConnectionType.None;
|
||||
/// <summary>
|
||||
/// 节点对应的方法入参所需参数来源
|
||||
/// </summary>
|
||||
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 第几个参数
|
||||
/// </summary>
|
||||
public int ArgIndex { get; set; } = -1;
|
||||
|
||||
public override bool IsCanUndo => false;
|
||||
|
||||
#region 私有参数
|
||||
private FlowCanvasDetails? FlowCanvas;
|
||||
private IFlowNode? FromNode;
|
||||
private IFlowNode? ToNode;
|
||||
#endregion
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (JunctionOfConnectionType == JunctionOfConnectionType.None)
|
||||
return false;
|
||||
if (JunctionOfConnectionType == JunctionOfConnectionType.Arg && ArgIndex == -1)
|
||||
return false;
|
||||
|
||||
if (!flowModelService.ContainsCanvasModel(CanvasGuid) // 不存在画布
|
||||
|| !flowModelService.ContainsNodeModel(FromNodeGuid) // 不存在节点
|
||||
|| !flowModelService.ContainsNodeModel(ToNodeGuid)) // 不存在节点
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false;
|
||||
if (!flowModelService.TryGetCanvasModel(CanvasGuid, out FlowCanvas) // 不存在画布
|
||||
|| !flowModelService.TryGetNodeModel(FromNodeGuid, out FromNode) // 不存在节点
|
||||
|| !flowModelService.TryGetNodeModel(ToNodeGuid, out ToNode)) // 不存在节点
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if(ChangeType == ConnectChangeType.Create) // 创建连线时需要检查
|
||||
{
|
||||
(var jcType, var isCanConnection) = CheckConnect(FromNode, ToNode, FromNodeJunctionType, ToNodeJunctionType);
|
||||
if (!isCanConnection)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "出现非预期的连接行为");
|
||||
return false; // 出现不符预期的连接行为,忽略此次连接行为
|
||||
}
|
||||
|
||||
// 如果起始控制点是“方法执行”,目标控制点是“方法调用”,需要反转 from to 节点
|
||||
if (jcType == JunctionOfConnectionType.Invoke
|
||||
&& FromNodeJunctionType == JunctionType.Execute
|
||||
&& ToNodeJunctionType == JunctionType.NextStep)
|
||||
{
|
||||
// 如果 起始控制点 是“方法调用”,需要反转 from to 节点
|
||||
(FromNode, ToNode) = (ToNode, FromNode);
|
||||
}
|
||||
|
||||
// 如果起始控制点是“方法入参”,目标控制点是“返回值”,需要反转 from to 节点
|
||||
if (jcType == JunctionOfConnectionType.Arg
|
||||
&& FromNodeJunctionType == JunctionType.ArgData
|
||||
&& ToNodeJunctionType == JunctionType.ReturnData)
|
||||
{
|
||||
(FromNode, ToNode) = (ToNode, FromNode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//if (toNode is SingleFlipflopNode flipflopNode)
|
||||
//{
|
||||
// flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除
|
||||
//}
|
||||
|
||||
var state = (JunctionOfConnectionType, ChangeType) switch
|
||||
{
|
||||
(JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Create) => await CreateInvokeConnection(), // 创建节点之间的调用关系
|
||||
(JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => await RemoveInvokeConnection(), // 移除节点之间的调用关系
|
||||
(JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Create) => await CreateArgConnection(), // 创建节点之间的参数传递关系
|
||||
(JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => await RemoveArgConnection(), // 移除节点之间的参数传递关系
|
||||
_ => false
|
||||
};
|
||||
return state;
|
||||
}
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建方法调用关系
|
||||
/// </summary>
|
||||
private async Task<bool> CreateInvokeConnection()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(FlowCanvas);
|
||||
ArgumentNullException.ThrowIfNull(FromNode);
|
||||
ArgumentNullException.ThrowIfNull(ToNode);
|
||||
IFlowNode fromNode = FromNode;
|
||||
IFlowNode toNode = ToNode;
|
||||
ConnectionInvokeType invokeType = ConnectionInvokeType;
|
||||
if (fromNode.ControlType == NodeControlType.FlowCall)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"流程接口节点不可调用下一个节点。" +
|
||||
$"{Environment.NewLine}流程节点:{fromNode.Guid}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var isOverwriting = false;
|
||||
ConnectionInvokeType overwritingCt = ConnectionInvokeType.None;
|
||||
var isPass = false;
|
||||
|
||||
#region 类型检查
|
||||
bool checkTypeState = true;
|
||||
List<ParameterDetails> toPds = new List<ParameterDetails>();
|
||||
|
||||
if (FromNode.ContainerNode is not null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,起始节点处于容器中");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ToNode.ContainerNode is not null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,目标节点处于容器中");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ToNode.ControlType is not (NodeControlType.GlobalData or NodeControlType.ExpCondition or NodeControlType.ExpOp))
|
||||
{
|
||||
|
||||
if (ToNode.MethodDetails.ParameterDetailss is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,目标节点没有入参参数。");
|
||||
return false;
|
||||
}
|
||||
if (ToNode.MethodDetails.ParameterDetailss.Length > 0)
|
||||
{
|
||||
var fromNoeReturnType = fromNode.MethodDetails.ReturnType;
|
||||
if (fromNoeReturnType != null
|
||||
&& fromNoeReturnType != typeof(object)
|
||||
&& fromNoeReturnType != typeof(void)
|
||||
&& fromNoeReturnType != typeof(Unit))
|
||||
{
|
||||
var toNodePds = toNode.MethodDetails.ParameterDetailss;
|
||||
foreach (ParameterDetails toNodePd in toNodePds)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(toNodePd.ArgDataSourceNodeGuid) // 入参没有设置数据来源节点
|
||||
&& toNodePd.DataType.IsAssignableFrom(fromNoeReturnType)) // 返回值与目标入参相同(或可转换为目标入参)
|
||||
{
|
||||
|
||||
toPds.Add(toNodePd);
|
||||
}
|
||||
}
|
||||
if (toPds.Count == 0)
|
||||
{
|
||||
var any = toNodePds.Any(pd => pd.ArgDataSourceNodeGuid == fromNode.Guid); // 判断目标节点是否已有该节点的连接
|
||||
checkTypeState = any;
|
||||
}
|
||||
else
|
||||
{
|
||||
checkTypeState = true; // 类型检查初步通过
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!checkTypeState) // 类型检查不通过
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, "连接失败,目标节点没有合适的入参接收返回值");
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
#region 检查是否存在对应的连接
|
||||
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
var count1 = fromNode.SuccessorNodes[ctType].Count(it => it.Guid.Equals(toNode.Guid));
|
||||
var count2 = toNode.PreviousNodes[ctType].Count(it => it.Guid.Equals(fromNode.Guid));
|
||||
var hasError1 = count1 > 0;
|
||||
var hasError2 = count2 > 0;
|
||||
if (hasError1 && hasError2)
|
||||
{
|
||||
if (ctType == invokeType)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"起始节点已与目标节点存在连接。" +
|
||||
$"{Environment.NewLine}起始节点:{fromNode.Guid}" +
|
||||
$"{Environment.NewLine}目标节点:{toNode.Guid}");
|
||||
return false;
|
||||
}
|
||||
isOverwriting = true; // 需要移除连接再创建连接
|
||||
overwritingCt = ctType;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 检查是否可能存在异常
|
||||
if (!hasError1 && hasError2)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" +
|
||||
$"{Environment.NewLine}起始节点:{fromNode.Guid}" +
|
||||
$"{Environment.NewLine}目标节点:{toNode.Guid}");
|
||||
isPass = false;
|
||||
}
|
||||
else if (hasError1 && !hasError2)
|
||||
{
|
||||
//
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" +
|
||||
$"{Environment.NewLine}起始节点:{fromNode.Guid}" +
|
||||
$"{Environment.NewLine}目标节点:{toNode.Guid}" +
|
||||
$"");
|
||||
isPass = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
isPass = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
if (isPass)
|
||||
{
|
||||
if (isOverwriting) // 需要替换
|
||||
{
|
||||
fromNode.SuccessorNodes[overwritingCt].Remove(toNode); // 从起始节点原有类别的子分支中移除
|
||||
toNode.PreviousNodes[overwritingCt].Remove(fromNode); // 从目标节点原有类别的父分支中移除
|
||||
}
|
||||
fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点新类别的子分支
|
||||
toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点新类别的父分支
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
fromNode.Guid, // 从哪个节点开始
|
||||
toNode.Guid, // 连接到那个节点
|
||||
JunctionOfConnectionType.Invoke,
|
||||
invokeType, // 连接线的样式类型
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||
));
|
||||
});
|
||||
|
||||
|
||||
/* foreach (var toPd in toPds)
|
||||
{
|
||||
string canvasGuid = CanvasGuid;
|
||||
string fromNodeGuid = fromNode.Guid;
|
||||
string toNodeGuid = toNode.Guid;
|
||||
JunctionType fromNodeJunctionType = JunctionType.ReturnData;
|
||||
JunctionType toNodeJunctionType = JunctionType.ArgData;
|
||||
ConnectionArgSourceType argSourceType = ConnectionArgSourceType.GetOtherNodeData;
|
||||
int argIndex = toPd.Index;
|
||||
// 调用创建连线接口
|
||||
flowEnvironment.FlowEdit.ConnectArgSourceNode(canvasGuid,
|
||||
fromNodeGuid,
|
||||
toNodeGuid,
|
||||
fromNodeJunctionType,
|
||||
toNodeJunctionType,
|
||||
argSourceType,
|
||||
argIndex);
|
||||
}*/
|
||||
|
||||
// Invoke
|
||||
// GetResult
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除方法调用关系
|
||||
/// </summary>
|
||||
private async Task<bool> RemoveInvokeConnection()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(FlowCanvas);
|
||||
ArgumentNullException.ThrowIfNull(FromNode);
|
||||
ArgumentNullException.ThrowIfNull(ToNode);
|
||||
|
||||
FromNode.SuccessorNodes[ConnectionInvokeType].Remove(ToNode);
|
||||
ToNode.PreviousNodes[ConnectionInvokeType].Remove(FromNode);
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
FromNode.Guid,
|
||||
ToNode.Guid,
|
||||
JunctionOfConnectionType.Invoke,
|
||||
ConnectionInvokeType,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove));
|
||||
});
|
||||
|
||||
|
||||
/* if (string.IsNullOrEmpty(ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = null;
|
||||
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
|
||||
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
UIContextOperation?.Invoke(() => Event.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
canvasGuid,
|
||||
fromNode.Guid,
|
||||
toNode.Guid,
|
||||
argIndex,
|
||||
JunctionOfConnectionType.Arg,
|
||||
ConnectionArgSourceType.GetPreviousNodeData,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove)));
|
||||
}*/
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建参数连接关系
|
||||
/// </summary>
|
||||
/// <exception cref="Exception"></exception>
|
||||
private async Task<bool> CreateArgConnection()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(FlowCanvas);
|
||||
ArgumentNullException.ThrowIfNull(FromNode);
|
||||
ArgumentNullException.ThrowIfNull(ToNode);
|
||||
|
||||
/*
|
||||
IFlowNode fromNodeControl = ToNode;
|
||||
IFlowNode toNodeControl = ToNode;*/
|
||||
ConnectionArgSourceType type = ConnectionArgSourceType;
|
||||
int index = ArgIndex;
|
||||
|
||||
|
||||
/*FromNode.NeedResultNodes[type].Remove(ToNode); // 从起始节点的参数来源中移除目标节点
|
||||
if (FromNode.Guid == ToNode.Guid) // 不能连接到自己
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"起始节点与目标节点不能是同一个节点" +
|
||||
$"{Environment.NewLine}起始节点:{FromNode.Guid}" +
|
||||
$"{Environment.NewLine}目标节点:{ToNode.Guid}");
|
||||
return false;
|
||||
}*/
|
||||
|
||||
if (FromNode.MethodDetails.ReturnType == typeof(void))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收 void 返回值。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
var toNodeArgSourceGuid = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid; // 目标节点对应参数可能已经有其它连接
|
||||
var toNodeArgSourceType = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
|
||||
|
||||
if (false && string.IsNullOrWhiteSpace(toNodeArgSourceGuid) && flowModelService.ContainsNodeModel(toNodeArgSourceGuid))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"连接失败,节点参数入参不允许接收多个节点返回值。起始节点[{FromNode.Guid}],目标节点[{FromNode.Guid}]。");
|
||||
return false;
|
||||
}
|
||||
if (FromNode.ContainerNode is not null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "连接失败,参数来源节点处于容器中");
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断是否建立过连接关系
|
||||
if (FromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == ConnectionArgSourceType)
|
||||
{
|
||||
if (FromNode.NeedResultNodes[type].Contains(ToNode)) // 如果来源节点被该节点获取过,则创建链接
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系" +
|
||||
$"起始节点:{FromNode.Guid}" +
|
||||
$"目标节点:{ToNode.Guid}" +
|
||||
$"参数索引:{ArgIndex}" +
|
||||
$"参数类型:{ConnectionArgSourceType}");
|
||||
return false;
|
||||
}
|
||||
|
||||
var fromNodeGuid = FromNode.Guid;
|
||||
var toNodeGuid = ToNode.Guid;
|
||||
// 目标节点需要参数,但却没有被依赖的记录,则添加依赖记录并出现连接
|
||||
FromNode.NeedResultNodes[type].Add(ToNode);
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
fromNodeGuid, // 从哪个节点开始
|
||||
toNodeGuid, // 连接到那个节点
|
||||
ArgIndex, // 连接线的样式类型
|
||||
JunctionOfConnectionType.Arg,
|
||||
ConnectionArgSourceType,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove // 是创建连接还是删除连接
|
||||
)); // 通知UI
|
||||
});
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
fromNodeGuid, // 从哪个节点开始
|
||||
toNodeGuid, // 连接到那个节点
|
||||
ArgIndex, // 连接线的样式类型
|
||||
JunctionOfConnectionType.Arg,
|
||||
ConnectionArgSourceType,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||
)); // 通知UI
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(toNodeArgSourceGuid)) // 参数入参节点已有来源,更改节点参数来源
|
||||
{
|
||||
var fromNodeGuid = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid;
|
||||
var toNodeGuid = ToNode.Guid;
|
||||
ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null;
|
||||
ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
|
||||
FromNode.NeedResultNodes[type].Remove(ToNode);
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
fromNodeGuid,
|
||||
toNodeGuid,
|
||||
ArgIndex,
|
||||
JunctionOfConnectionType.Arg,
|
||||
ConnectionArgSourceType.GetPreviousNodeData,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove));
|
||||
});
|
||||
}
|
||||
var toNodePd = ToNode.MethodDetails.ParameterDetailss[ArgIndex];
|
||||
toNodePd.ArgDataSourceNodeGuid = FromNode.Guid;
|
||||
toNodePd.ArgDataSourceType = ConnectionArgSourceType;
|
||||
FromNode.NeedResultNodes[type].Add(ToNode);
|
||||
toNodePd.IsExplicitData = false;
|
||||
if (ToNode.ControlType == NodeControlType.Script)
|
||||
{
|
||||
// 脚本节点入参确定/改变来源时,更改对应的入参数据类型
|
||||
toNodePd.DataType = FromNode.MethodDetails.ReturnType;
|
||||
}
|
||||
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
FromNode.Guid, // 从哪个节点开始
|
||||
ToNode.Guid, // 连接到那个节点
|
||||
ArgIndex, // 连接线的样式类型
|
||||
JunctionOfConnectionType.Arg,
|
||||
ConnectionArgSourceType,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||
)); // 通知UI
|
||||
});
|
||||
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除参数连接关系
|
||||
/// </summary>
|
||||
private async Task<bool> RemoveArgConnection()
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(FlowCanvas);
|
||||
ArgumentNullException.ThrowIfNull(FromNode);
|
||||
ArgumentNullException.ThrowIfNull(ToNode);
|
||||
|
||||
if (ToNode.MethodDetails.ParameterDetailss is null) return false;
|
||||
var type = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
|
||||
FromNode.NeedResultNodes[type].Remove(ToNode);
|
||||
ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = string.Empty;
|
||||
ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
|
||||
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(
|
||||
new NodeConnectChangeEventArgs(
|
||||
FlowCanvas.Guid,
|
||||
FromNode.Guid,
|
||||
ToNode.Guid,
|
||||
ArgIndex,
|
||||
JunctionOfConnectionType.Arg,
|
||||
ConnectionArgSourceType.GetPreviousNodeData,
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove));
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查连接是否合法
|
||||
/// </summary>
|
||||
/// <param name="fromNode">发起连接的起始节点</param>
|
||||
/// <param name="toNode">要连接的目标节点</param>
|
||||
/// <param name="fromNodeJunctionType">发起连接节点的控制点类型</param>
|
||||
/// <param name="toNodeJunctionType">被连接节点的控制点类型</param>
|
||||
/// <returns></returns>
|
||||
public static (JunctionOfConnectionType, bool) CheckConnect(IFlowNode fromNode,
|
||||
IFlowNode toNode,
|
||||
JunctionType fromNodeJunctionType,
|
||||
JunctionType toNodeJunctionType)
|
||||
{
|
||||
var type = JunctionOfConnectionType.None;
|
||||
var state = false;
|
||||
if (fromNodeJunctionType == JunctionType.Execute)
|
||||
{
|
||||
if (toNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid))
|
||||
{
|
||||
// “方法执行”控制点拖拽到“下一节点”控制点,且不是同一个节点, 添加方法执行关系
|
||||
type = JunctionOfConnectionType.Invoke;
|
||||
state = true;
|
||||
}
|
||||
//else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid))
|
||||
//{
|
||||
// // “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata
|
||||
// type = JunctionOfConnectionType.Arg;
|
||||
// state = true;
|
||||
//}
|
||||
}
|
||||
else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid))
|
||||
{
|
||||
// “下一节点”控制点只能拖拽到“方法执行”控制点,且不能是同一个节点
|
||||
if (toNodeJunctionType == JunctionType.Execute && !fromNode.Guid.Equals(toNode.Guid))
|
||||
{
|
||||
type = JunctionOfConnectionType.Invoke;
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
else if (fromNodeJunctionType == JunctionType.ArgData)
|
||||
{
|
||||
//if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系
|
||||
//{
|
||||
// // “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata
|
||||
// type = JunctionOfConnectionType.Arg;
|
||||
// state = true;
|
||||
//}
|
||||
if (toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid))
|
||||
{
|
||||
// “”控制点拖拽到“方法返回值”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata
|
||||
type = JunctionOfConnectionType.Arg;
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
else if (fromNodeJunctionType == JunctionType.ReturnData)
|
||||
{
|
||||
if (toNodeJunctionType == JunctionType.ArgData && !fromNode.Guid.Equals(toNode.Guid))
|
||||
{
|
||||
// “方法返回值”控制点拖拽到“方法入参”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata
|
||||
type = JunctionOfConnectionType.Arg;
|
||||
state = true;
|
||||
}
|
||||
}
|
||||
// 剩下的情况都是不符预期的连接行为,忽略。
|
||||
return (type, state);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
91
NodeFlow/Model/Operations/ChangeParameterOperation.cs
Normal file
91
NodeFlow/Model/Operations/ChangeParameterOperation.cs
Normal file
@@ -0,0 +1,91 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
internal class ChangeParameterOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(ChangeParameterOperation);
|
||||
|
||||
public string NodeGuid { get; set; }
|
||||
public bool IsAdd{ get; set; }
|
||||
|
||||
public int ParamIndex { get; set; }
|
||||
|
||||
|
||||
private IFlowNode nodeModel;
|
||||
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (!flowModelService.TryGetNodeModel(NodeGuid, out var nodeModel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
this.nodeModel = nodeModel;
|
||||
|
||||
var pds = nodeModel.MethodDetails.ParameterDetailss;
|
||||
var parameterCount = pds.Length;
|
||||
if (ParamIndex >= parameterCount)
|
||||
{
|
||||
return false; // 需要被添加的下标索引大于入参数组的长度
|
||||
}
|
||||
if (IsAdd)
|
||||
{
|
||||
if (pds[ParamIndex].IsParams == false)
|
||||
{
|
||||
return false; // 对应的入参并非可选参数中的一部分
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override Task<bool> ExecuteAsync()
|
||||
{
|
||||
|
||||
if (!ValidationParameter()) return Task.FromResult(false);
|
||||
|
||||
if (IsAdd)
|
||||
{
|
||||
if (nodeModel.MethodDetails.AddParamsArg(ParamIndex))
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (nodeModel.MethodDetails.RemoveParamsArg(ParamIndex))
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
113
NodeFlow/Model/Operations/ContainerPlaceNodeOperation.cs
Normal file
113
NodeFlow/Model/Operations/ContainerPlaceNodeOperation.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using Microsoft.VisualBasic.FileIO;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
/// <summary>
|
||||
/// 放置节点操作
|
||||
/// </summary>
|
||||
internal class ContainerPlaceNodeOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(ContainerPlaceNodeOperation);
|
||||
|
||||
/// <summary>
|
||||
/// 所在画布
|
||||
/// </summary>
|
||||
public string CanvasGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子节点,该数据为此次事件的主节点
|
||||
/// </summary>
|
||||
public string NodeGuid { get; set; }
|
||||
/// <summary>
|
||||
/// 父节点
|
||||
/// </summary>
|
||||
public string ContainerNodeGuid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 父节点
|
||||
/// </summary>
|
||||
private INodeContainer ContainerNode;
|
||||
|
||||
/// <summary>
|
||||
/// 子节点,该数据为此次事件的主节点
|
||||
/// </summary>
|
||||
private IFlowNode Node;
|
||||
|
||||
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (!flowModelService.ContainsCanvasModel(CanvasGuid))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 获取目标节点与容器节点
|
||||
if (!flowModelService.TryGetNodeModel(NodeGuid, out var nodeModel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!flowModelService.TryGetNodeModel(ContainerNodeGuid, out var containerNode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (nodeModel.ContainerNode is INodeContainer tmpContainer)
|
||||
{
|
||||
//SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((IFlowNode)tmpContainer).Guid}]");
|
||||
return false;
|
||||
}
|
||||
if(containerNode is not INodeContainer containerNode2)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Node = nodeModel;
|
||||
ContainerNode = containerNode2;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false;
|
||||
var isSuccess = ContainerNode.PlaceNode(Node);
|
||||
if(isSuccess is true)
|
||||
{
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, ContainerNodeGuid)); // 通知UI更改节点放置位置
|
||||
});
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
public override bool Undo()
|
||||
{
|
||||
var isSuccess = ContainerNode.TakeOutNode(Node);
|
||||
if (isSuccess is true)
|
||||
{
|
||||
_ = TriggerEvent(() =>
|
||||
{
|
||||
// 取出节点,重新放置在画布上
|
||||
flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, ContainerNode.Guid, NodeGuid));
|
||||
});
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
98
NodeFlow/Model/Operations/ContainerTakeOutNodeOperation.cs
Normal file
98
NodeFlow/Model/Operations/ContainerTakeOutNodeOperation.cs
Normal file
@@ -0,0 +1,98 @@
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 取出节点操作
|
||||
/// </summary>
|
||||
internal class ContainerTakeOutNodeOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(ContainerTakeOutNodeOperation);
|
||||
|
||||
/// <summary>
|
||||
/// 所在画布
|
||||
/// </summary>
|
||||
public string CanvasGuid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 子节点,该数据为此次事件的主节点
|
||||
/// </summary>
|
||||
public string NodeGuid { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 父节点
|
||||
/// </summary>
|
||||
private INodeContainer ContainerNode;
|
||||
|
||||
/// <summary>
|
||||
/// 子节点,该数据为此次事件的主节点
|
||||
/// </summary>
|
||||
private IFlowNode Node;
|
||||
|
||||
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (!flowModelService.ContainsCanvasModel(CanvasGuid))
|
||||
{
|
||||
flowEnvironment.WriteLine(Serein.Library.InfoType.WARN, $"节点取出失败,目标画布不存在[{NodeGuid}]");
|
||||
return false;
|
||||
}
|
||||
// 获取目标节点与容器节点
|
||||
if (!flowModelService.TryGetNodeModel(NodeGuid, out var nodeModel))
|
||||
{
|
||||
flowEnvironment.WriteLine(Serein.Library.InfoType.WARN, $"节点取出失败,目标节点不存在[{NodeGuid}]");
|
||||
return false;
|
||||
}
|
||||
if (nodeModel.ContainerNode is not INodeContainer containerNode)
|
||||
{
|
||||
flowEnvironment.WriteLine(Serein.Library.InfoType.WARN, $"节点取出失败,节点并非容器节点[{nodeModel.Guid}]");
|
||||
return false;
|
||||
}
|
||||
Node = nodeModel;
|
||||
ContainerNode = containerNode;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false;
|
||||
|
||||
var isSuccess = ContainerNode.TakeOutNode(Node);
|
||||
if (isSuccess is true)
|
||||
{
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
// 取出节点,重新放置在画布上
|
||||
flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, ContainerNode.Guid, NodeGuid));
|
||||
});
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
public override bool Undo()
|
||||
{
|
||||
var isSuccess = ContainerNode.PlaceNode(Node);
|
||||
if (isSuccess is true)
|
||||
{
|
||||
if (ContainerNode is IFlowNode containerFlowNode)
|
||||
{
|
||||
flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, containerFlowNode.Guid)); // 通知UI更改节点放置位置
|
||||
}
|
||||
}
|
||||
return isSuccess;
|
||||
}
|
||||
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
56
NodeFlow/Model/Operations/CreateCanvasOperation.cs
Normal file
56
NodeFlow/Model/Operations/CreateCanvasOperation.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
internal class CreateCanvasOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(CreateCanvasOperation);
|
||||
public override bool IsCanUndo => false;
|
||||
|
||||
public required FlowCanvasDetailsInfo CanvasInfo { get; set; }
|
||||
|
||||
|
||||
|
||||
private FlowCanvasDetails? flowCanvasDetails;
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (CanvasInfo is null)
|
||||
return false; // 没有必须的参数
|
||||
if (string.IsNullOrEmpty(CanvasInfo.Guid))
|
||||
return false; // 不能没有Guid
|
||||
if(flowModelService.ContainsCanvasModel(CanvasInfo.Guid))
|
||||
return false; // 画布已存在
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if(!ValidationParameter()) return false;
|
||||
|
||||
var cavasnModel = new FlowCanvasDetails(flowEnvironment);
|
||||
cavasnModel.LoadInfo(CanvasInfo);
|
||||
flowModelService.AddCanvasModel(cavasnModel);
|
||||
this.flowCanvasDetails = cavasnModel; ;
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnCanvasCreated(new CanvasCreateEventArgs(cavasnModel));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
148
NodeFlow/Model/Operations/CreateNodeOperation.cs
Normal file
148
NodeFlow/Model/Operations/CreateNodeOperation.cs
Normal file
@@ -0,0 +1,148 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Services;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
internal class CreateNodeOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(CreateNodeOperation);
|
||||
|
||||
public required string CanvasGuid { get; set; }
|
||||
public required NodeControlType NodeControlType { get; set; }
|
||||
public required PositionOfUI Position { get; set; }
|
||||
public required MethodDetailsInfo? MethodDetailsInfo { get; set; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否为基础节点
|
||||
/// </summary>
|
||||
private bool IsBaseNode => NodeControlType.IsBaseNode();
|
||||
|
||||
/// <summary>
|
||||
/// 执行成功后所创建的节点
|
||||
/// </summary>
|
||||
private IFlowNode? flowNode;
|
||||
|
||||
/// <summary>
|
||||
/// 节点所在画布
|
||||
/// </summary>
|
||||
private FlowCanvasDetails flowCanvasDetails;
|
||||
|
||||
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
// 检查是否存在画布
|
||||
|
||||
var canvasModel = flowModelService.GetCanvasModel(CanvasGuid);
|
||||
if(canvasModel is null)
|
||||
return false;
|
||||
|
||||
// 检查类型(防非预期的调用)
|
||||
if (NodeControlType == NodeControlType.None)
|
||||
return false;
|
||||
|
||||
// 检查放置位置是否超限(防非预期的调用)
|
||||
if (Position.X < 0 || Position.Y < 0
|
||||
|| Position.X > canvasModel.Width
|
||||
|| Position.Y > canvasModel.Height)
|
||||
return false;
|
||||
|
||||
// 所创建的节点并非基础节点,却没有传入方法信息,将会导致创建失败
|
||||
if (!IsBaseNode && MethodDetailsInfo is null)
|
||||
return false;
|
||||
|
||||
// 缓存画布model,提高性能
|
||||
this.flowCanvasDetails = canvasModel;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false; // 执行时验证
|
||||
|
||||
IFlowNode? nodeModel;
|
||||
if (IsBaseNode)
|
||||
{
|
||||
nodeModel = FlowNodeExtension.CreateNode(flowEnvironment.IOC, NodeControlType); // 加载基础节点
|
||||
}
|
||||
else
|
||||
{
|
||||
if(MethodDetailsInfo is null)
|
||||
{
|
||||
return false;
|
||||
//throw new InvalidOperationException($"无法创建节点,因为MethodDetailsInfo属性为null");
|
||||
}
|
||||
if (!flowLibraryManagement.TryGetMethodDetails(MethodDetailsInfo.AssemblyName, // 创建节点
|
||||
MethodDetailsInfo.MethodName,
|
||||
out var methodDetails))
|
||||
{
|
||||
return false;
|
||||
//throw new InvalidOperationException($"无法创建节点,因为没有找到{MethodDetailsInfo.AssemblyName}.{MethodDetailsInfo.MethodName}方法,请检查是否已加载对应程序集");
|
||||
}
|
||||
nodeModel = FlowNodeExtension.CreateNode(flowEnvironment.IOC, NodeControlType, methodDetails); // 一般的加载节点方法
|
||||
}
|
||||
|
||||
nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||
nodeModel.Position = Position; // 设置位置
|
||||
|
||||
// 节点与画布互相绑定
|
||||
nodeModel.CanvasDetails = flowCanvasDetails;
|
||||
flowCanvasDetails.Nodes = [..flowCanvasDetails.Nodes, nodeModel];
|
||||
|
||||
flowModelService.AddNodeModel(nodeModel);
|
||||
this.flowNode = nodeModel;
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeCreated(new NodeCreateEventArgs(flowCanvasDetails.Guid, nodeModel, Position));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override bool Undo()
|
||||
{
|
||||
if (!ValidationParameter()) return false; // 撤销时验证
|
||||
if(flowNode is null) return false; // 没有创建过节点
|
||||
var canvasGuid = flowCanvasDetails.Guid;
|
||||
var nodeGuid = flowNode.Guid;
|
||||
flowEnvironment.FlowEdit.RemoveNode(canvasGuid, nodeGuid);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
/*private bool TryAddNode(IFlowNode nodeModel)
|
||||
{
|
||||
nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||
NodeModels.TryAdd(nodeModel.Guid, nodeModel);
|
||||
|
||||
|
||||
// 如果是触发器,则需要添加到专属集合中
|
||||
if (nodeModel is SingleFlipflopNode flipflopNode)
|
||||
{
|
||||
var guid = flipflopNode.Guid;
|
||||
if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
||||
{
|
||||
FlipflopNodes.Add(flipflopNode);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}*/
|
||||
}
|
||||
}
|
||||
126
NodeFlow/Model/Operations/OperationBase.cs
Normal file
126
NodeFlow/Model/Operations/OperationBase.cs
Normal file
@@ -0,0 +1,126 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Services;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
internal interface IOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// 用于判断是否可以撤销
|
||||
/// </summary>
|
||||
bool IsCanUndo { get; }
|
||||
/// <summary>
|
||||
/// 执行操作前验证数据
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool ValidationParameter();
|
||||
/// <summary>
|
||||
/// 执行操作
|
||||
/// </summary>
|
||||
Task<bool> ExecuteAsync();
|
||||
/// <summary>
|
||||
/// 撤销操作
|
||||
/// </summary>
|
||||
bool Undo();
|
||||
}
|
||||
|
||||
internal abstract class OperationBase : IOperation
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行环境
|
||||
/// </summary>
|
||||
[AutoInjection]
|
||||
protected IFlowEnvironment flowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 节点管理服务
|
||||
/// </summary>
|
||||
[AutoInjection]
|
||||
protected FlowModelService flowModelService;
|
||||
|
||||
/// <summary>
|
||||
/// 节点管理服务
|
||||
/// </summary>
|
||||
[AutoInjection]
|
||||
protected UIContextOperation uiContextOperation;
|
||||
|
||||
/// <summary>
|
||||
/// 流程依赖服务
|
||||
/// </summary>
|
||||
[AutoInjection]
|
||||
protected FlowLibraryService flowLibraryManagement;
|
||||
|
||||
/// <summary>
|
||||
/// 流程事件服务
|
||||
/// </summary>
|
||||
[AutoInjection]
|
||||
protected IFlowEnvironmentEvent flowEnvironmentEvent;
|
||||
|
||||
|
||||
public abstract string Theme { get;}
|
||||
|
||||
/// <summary>
|
||||
/// 是否支持特效
|
||||
/// </summary>
|
||||
public virtual bool IsCanUndo => true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 验证参数
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract bool ValidationParameter();
|
||||
|
||||
/// <summary>
|
||||
/// 执行
|
||||
/// </summary>
|
||||
public abstract Task<bool> ExecuteAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 撤销
|
||||
/// </summary>
|
||||
public virtual bool Undo()
|
||||
{
|
||||
if (!IsCanUndo)
|
||||
{
|
||||
Debug.WriteLine($"该操作暂未提供撤销功能[{Theme}]");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 导出操作信息
|
||||
/// </summary>
|
||||
public abstract void ToInfo();
|
||||
|
||||
|
||||
protected async Task TriggerEvent(Action action) => await SereinEnv.TriggerEvent(action);
|
||||
|
||||
}
|
||||
|
||||
internal class OperationInfo
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
class Test {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
64
NodeFlow/Model/Operations/RemoveCanvasOperation.cs
Normal file
64
NodeFlow/Model/Operations/RemoveCanvasOperation.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Model.Nodes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
internal class RemoveCanvasOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(RemoveCanvasOperation);
|
||||
public override bool IsCanUndo => false;
|
||||
public required string CanvasGuid { get; set; }
|
||||
|
||||
private FlowCanvasDetailsInfo? flowCanvasDetailsInfo;
|
||||
private FlowCanvasDetails? flowCanvasDetails;
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
var canvasModel = flowModelService.GetCanvasModel(CanvasGuid);
|
||||
if (canvasModel is null) return false; // 画布不存在
|
||||
var nodeCount = canvasModel.Nodes.Count;
|
||||
if (nodeCount > 0)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, "无法删除具有节点的画布");
|
||||
return false;
|
||||
}
|
||||
this.flowCanvasDetails = canvasModel;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false;
|
||||
|
||||
if (flowCanvasDetails is null)
|
||||
{
|
||||
// 验证过画布存在,但这时画布不存在了
|
||||
// 考虑到多线程操作影响,一般不会进入这个逻辑分支
|
||||
var canvasModel = flowModelService.GetCanvasModel(CanvasGuid);
|
||||
if (canvasModel is null) return false; // 画布不存在
|
||||
flowCanvasDetails = canvasModel;
|
||||
}
|
||||
|
||||
flowModelService.RemoveCanvasModel(flowCanvasDetails);
|
||||
flowCanvasDetailsInfo = flowCanvasDetails.ToInfo();
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnCanvasRemoved(new CanvasRemoveEventArgs(flowCanvasDetails.Guid));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
284
NodeFlow/Model/Operations/RemoveNodeOperation.cs
Normal file
284
NodeFlow/Model/Operations/RemoveNodeOperation.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using System.Reflection.Metadata;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
internal class RemoveNodeOperation : OperationBase
|
||||
{
|
||||
public override string Theme => throw new NotImplementedException();
|
||||
|
||||
public required string CanvasGuid { get; internal set; }
|
||||
public required string NodeGuid { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点所在画布
|
||||
/// </summary>
|
||||
private FlowCanvasDetails flowCanvasDetails;
|
||||
|
||||
/// <summary>
|
||||
/// 被删除的节点
|
||||
/// </summary>
|
||||
private IFlowNode flowNode;
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点时删除连线所触发的事件参数的缓存
|
||||
/// </summary>
|
||||
private List<NodeConnectChangeEventArgs> EventArgs { get; } = new List<NodeConnectChangeEventArgs>();
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
var canvasModel = flowModelService.GetCanvasModel(CanvasGuid);
|
||||
var nodeModel = flowModelService.GetNodeModel(NodeGuid);
|
||||
if(canvasModel is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if(nodeModel is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
flowCanvasDetails = canvasModel;
|
||||
flowNode = nodeModel;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false;
|
||||
|
||||
// 需要移除对应的方法调用、参数获取调用以及子节点信息
|
||||
// 还需要记录移除的事件参数,用以撤销恢复
|
||||
|
||||
if (flowNode.ChildrenNode.Count > 0)
|
||||
{
|
||||
// 如果该节点存在子节点,则删除所有子节点
|
||||
foreach(var child in flowNode.ChildrenNode)
|
||||
{
|
||||
flowEnvironment.FlowEdit.RemoveNode(CanvasGuid, child.Guid);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region 移除方法调用关系
|
||||
|
||||
// 检查该节点的前继节点,然后从这些前继节点中移除与该节点的连接关系
|
||||
var previousNodes = flowNode.PreviousNodes.Where(kvp => kvp.Value.Count > 0).ToDictionary();
|
||||
foreach (var item in previousNodes)
|
||||
{
|
||||
|
||||
var connectionType = item.Key; // 连接类型
|
||||
var nodes = item.Value.ToArray(); // 对应类型的父节点集合
|
||||
foreach (IFlowNode previousNode in nodes)
|
||||
{
|
||||
flowNode.PreviousNodes[connectionType].Remove(previousNode);
|
||||
previousNode.SuccessorNodes[connectionType].Remove(flowNode);
|
||||
var e = new NodeConnectChangeEventArgs(
|
||||
CanvasGuid, // 画布
|
||||
previousNode.Guid, // 父节点Guid
|
||||
flowNode.Guid, // 被移除的节点Guid
|
||||
JunctionOfConnectionType.Invoke, // 方法调用关系
|
||||
connectionType, // 对应的连接关系
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
|
||||
EventArgs.Add(e); // 缓存事件参数
|
||||
}
|
||||
}
|
||||
|
||||
// 检查该节点的后续节点,然后从这些后续节点中移除与该节点的连接关系
|
||||
var successorNodes = flowNode.SuccessorNodes.Where(kvp => kvp.Value.Count > 0).ToDictionary();
|
||||
if (flowNode.ControlType == NodeControlType.FlowCall)
|
||||
{
|
||||
// 根据流程接口节点目前的设计,暂未支持能连接下一个节点
|
||||
}
|
||||
else
|
||||
{
|
||||
// 遍历所有后继节点,从那些后继节点中的前置节点集合中移除该节点
|
||||
foreach (var item in successorNodes)
|
||||
{
|
||||
|
||||
var connectionType = item.Key; // 方法调用连接类型
|
||||
var nodes = item.Value.ToArray(); // 对应类型的父节点集合
|
||||
foreach (IFlowNode successorNode in nodes)
|
||||
{
|
||||
successorNode.PreviousNodes[connectionType].Remove(flowNode);
|
||||
flowNode.SuccessorNodes[connectionType].Remove(successorNode);
|
||||
var e = new NodeConnectChangeEventArgs(
|
||||
CanvasGuid, // 画布
|
||||
flowNode.Guid, // 被移除的节点Guid
|
||||
successorNode.Guid, // 子节点Guid
|
||||
JunctionOfConnectionType.Invoke, // 方法调用关系
|
||||
connectionType, // 对应的连接关系
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
|
||||
EventArgs.Add(e); // 缓存事件参数
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 移除参数获取关系
|
||||
|
||||
// 遍历需要该节点返回值的节点,移除与其的连接
|
||||
foreach (var item in flowNode.NeedResultNodes)
|
||||
{
|
||||
var connectionType = item.Key; // 参数来源连接类型
|
||||
var argNodes = item.Value.ToArray(); // 对应类型的入参需求节点集合
|
||||
foreach (var argNode in argNodes)
|
||||
{
|
||||
var md = argNode.MethodDetails;
|
||||
if (md is null) continue;
|
||||
var pds = md.ParameterDetailss;
|
||||
if (pds is null || pds.Length == 0) continue;
|
||||
foreach (var parameter in pds)
|
||||
{
|
||||
if (!parameter.ArgDataSourceNodeGuid.Equals(flowNode.Guid)) continue;
|
||||
parameter.ArgDataSourceNodeGuid = string.Empty;
|
||||
parameter.ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData;
|
||||
// 找到了对应的入参控制点了
|
||||
var e = new NodeConnectChangeEventArgs(
|
||||
CanvasGuid, // 画布
|
||||
flowNode.Guid, // 数据来源节点(被移除的节点Guid)
|
||||
argNode.Guid, // 需要数据的节点
|
||||
parameter.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
|
||||
JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
|
||||
connectionType, // 对应的连接关系
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
|
||||
EventArgs.Add(e); // 缓存事件参数
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 遍历该节点参数详情,获取来源节点,移除与其的连接
|
||||
|
||||
if (flowNode.MethodDetails?.ParameterDetailss != null)
|
||||
{
|
||||
var pds = flowNode.MethodDetails.ParameterDetailss.ToArray();
|
||||
foreach (var pd in pds)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(pd.ArgDataSourceNodeGuid)) continue;
|
||||
if(flowModelService.TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var argSourceNode))
|
||||
{
|
||||
pd.ArgDataSourceNodeGuid = string.Empty;
|
||||
pd.ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData;
|
||||
// 找到了对应的入参控制点了
|
||||
var e = new NodeConnectChangeEventArgs(
|
||||
CanvasGuid, // 画布
|
||||
argSourceNode.Guid, // 数据来源节点
|
||||
flowNode.Guid, // 需要数据的节点(被移除的节点Guid)
|
||||
pd.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
|
||||
JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
|
||||
pd.ArgDataSourceType, // 对应的连接关系
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
|
||||
EventArgs.Add(e); // 缓存事件参数
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
flowModelService.RemoveNodeModel(flowNode); // 从记录中移除
|
||||
//flowNode.Remove(); // 调用节点的移除方法
|
||||
|
||||
|
||||
// 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上
|
||||
// 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行
|
||||
NodeConnectChangeEventArgs[] es = EventArgs.ToArray();
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
|
||||
/*flowCanvasDetails.Nodes.Remove(flowNode);
|
||||
flowCanvasDetails.OnPropertyChanged(nameof(FlowCanvasDetails.Nodes));
|
||||
if (flowNode.IsPublic)
|
||||
{
|
||||
flowCanvasDetails.PublicNodes.Remove(flowNode);
|
||||
flowCanvasDetails.OnPropertyChanged(nameof(FlowCanvasDetails.PublicNodes));
|
||||
}*/
|
||||
|
||||
// 手动赋值刷新UI显示
|
||||
var lsit = flowCanvasDetails.Nodes.ToList();
|
||||
lsit.Remove(flowNode);
|
||||
flowCanvasDetails.Nodes = lsit;
|
||||
if (flowNode.IsPublic)
|
||||
{
|
||||
var publicNodes = flowCanvasDetails.PublicNodes.ToList();
|
||||
publicNodes.Remove(flowNode);
|
||||
flowCanvasDetails.PublicNodes = publicNodes;
|
||||
}
|
||||
|
||||
foreach (var e in es)
|
||||
{
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(e); // 触发事件
|
||||
}
|
||||
flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid));
|
||||
});
|
||||
|
||||
/*if (flowEnvironment.UIContextOperation is null)
|
||||
{
|
||||
flowCanvasDetails?.Nodes.Remove(flowNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
|
||||
}*/
|
||||
|
||||
/* await TriggerEvent(() =>
|
||||
{
|
||||
|
||||
});*/
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Undo()
|
||||
{
|
||||
// 先恢复被删除的节点
|
||||
|
||||
|
||||
|
||||
// 撤销删除节点时,还需要恢复连线状态
|
||||
foreach (NodeConnectChangeEventArgs e in EventArgs)
|
||||
{
|
||||
NodeConnectChangeEventArgs? newEventArgs = null;
|
||||
if (e.JunctionOfConnectionType == JunctionOfConnectionType.Invoke)
|
||||
{
|
||||
newEventArgs = new NodeConnectChangeEventArgs(
|
||||
e.CanvasGuid, // 画布
|
||||
e.FromNodeGuid, // 被移除的节点Guid
|
||||
e.ToNodeGuid, // 子节点Guid
|
||||
e.JunctionOfConnectionType, // 指示需要恢复的是方法调用线
|
||||
e.ConnectionInvokeType, // 对应的连接关系
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create); // 创建连线
|
||||
}
|
||||
else if (e.JunctionOfConnectionType == JunctionOfConnectionType.Arg)
|
||||
{
|
||||
newEventArgs = new NodeConnectChangeEventArgs(
|
||||
e.CanvasGuid, // 画布
|
||||
e.FromNodeGuid, // 被移除的节点Guid
|
||||
e.ToNodeGuid, // 子节点Guid
|
||||
e.ArgIndex, // 作用在第几个参数上,用于指示移除第几个参数的连线
|
||||
e.JunctionOfConnectionType, // 指示需要恢复的是参数连接线
|
||||
e.ConnectionArgSourceType, // 对应的连接关系
|
||||
NodeConnectChangeEventArgs.ConnectChangeType.Create); // 创建连线
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
newEventArgs = null;
|
||||
}
|
||||
if (newEventArgs != null)
|
||||
{
|
||||
// 使用反转了的事件参数进行触发
|
||||
flowEnvironmentEvent.OnNodeConnectChanged(newEventArgs);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,91 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 将调用顺序置为优先
|
||||
/// </summary>
|
||||
internal class SetConnectPriorityInvokeOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(SetConnectPriorityInvokeOperation);
|
||||
|
||||
public string FromNodeGuid { get; set; }
|
||||
public string ToNodeGuid { get; set; }
|
||||
public ConnectionInvokeType ConnectionType { get; set; }
|
||||
|
||||
private IFlowNode FromNode;
|
||||
private IFlowNode ToNode;
|
||||
private int lastIdx = -1;
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (ConnectionType == ConnectionInvokeType.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 获取起始节点与目标节点
|
||||
if (!flowModelService.TryGetNodeModel(FromNodeGuid, out var fromNode) || !flowModelService.TryGetNodeModel(ToNodeGuid, out var toNode))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fromNode is null || toNode is null) return false;
|
||||
|
||||
FromNode = fromNode;
|
||||
ToNode = toNode;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 成为首项
|
||||
/// </summary>
|
||||
public override Task<bool> ExecuteAsync()
|
||||
{
|
||||
if(!ValidationParameter()) return Task.FromResult(false);
|
||||
|
||||
if (FromNode.SuccessorNodes.TryGetValue(ConnectionType, out var nodes))
|
||||
{
|
||||
var idx = nodes.IndexOf(ToNode);
|
||||
if (idx > -1)
|
||||
{
|
||||
lastIdx = idx;
|
||||
nodes.RemoveAt(idx);
|
||||
nodes.Insert(0, ToNode);
|
||||
}
|
||||
}
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复原来的位置
|
||||
/// </summary>
|
||||
public override bool Undo()
|
||||
{
|
||||
if (FromNode.SuccessorNodes.TryGetValue(ConnectionType, out var nodes))
|
||||
{
|
||||
var idx = nodes.IndexOf(ToNode);
|
||||
if (idx > -1)
|
||||
{
|
||||
nodes.RemoveAt(idx);
|
||||
nodes.Insert(lastIdx, ToNode);
|
||||
lastIdx = 0;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
82
NodeFlow/Model/Operations/SetStartNodeOperation.cs
Normal file
82
NodeFlow/Model/Operations/SetStartNodeOperation.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Model.Operations
|
||||
{
|
||||
/// <summary>
|
||||
/// 设置起始节点
|
||||
/// </summary>
|
||||
internal class SetStartNodeOperation : OperationBase
|
||||
{
|
||||
public override string Theme => nameof(SetStartNodeOperation);
|
||||
|
||||
|
||||
public string CanvasGuid { get; set; }
|
||||
public string NewNodeGuid { get; set; }
|
||||
|
||||
|
||||
private FlowCanvasDetails CanvasModel;
|
||||
private IFlowNode NewStartNodeModel;
|
||||
private IFlowNode? OldStartNodeModel;
|
||||
|
||||
|
||||
public override bool ValidationParameter()
|
||||
{
|
||||
if (!flowModelService.TryGetCanvasModel(CanvasGuid, out CanvasModel)
|
||||
|| !flowModelService.TryGetNodeModel(NewNodeGuid, out NewStartNodeModel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public override async Task<bool> ExecuteAsync()
|
||||
{
|
||||
if (!ValidationParameter()) return false;
|
||||
|
||||
if (CanvasModel.StartNode is not null
|
||||
&& flowModelService.TryGetNodeModel(CanvasModel.StartNode.Guid, out var flowNode))
|
||||
{
|
||||
OldStartNodeModel = flowNode;
|
||||
}
|
||||
|
||||
CanvasModel.StartNode = NewStartNodeModel;
|
||||
|
||||
await TriggerEvent(() =>
|
||||
{
|
||||
flowEnvironmentEvent.OnStartNodeChanged(new StartNodeChangeEventArgs(CanvasModel.Guid, OldStartNodeModel?.Guid, NewStartNodeModel.Guid));
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Undo()
|
||||
{
|
||||
if(OldStartNodeModel is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var newStartNode = OldStartNodeModel;
|
||||
var oldStartNode = NewStartNodeModel;
|
||||
|
||||
NewStartNodeModel = newStartNode;
|
||||
OldStartNodeModel = oldStartNode;
|
||||
CanvasModel.StartNode = oldStartNode;
|
||||
|
||||
flowEnvironmentEvent.OnStartNodeChanged(new StartNodeChangeEventArgs(CanvasModel.Guid, oldStartNode?.Guid, newStartNode.Guid));
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
public override void ToInfo()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user