using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Operation;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using static Serein.Library.Api.IFlowEnvironment;
namespace Serein.NodeFlow.Env
{
///
/// 流程编辑接口实现
///
internal class FlowEdit : IFlowEdit
{
public FlowEdit(IFlowEnvironment flowEnvironment,
IFlowEnvironmentEvent flowEnvironmentEvent,
FlowLibraryService flowLibraryManagement,
FlowOperationService flowOperationService,
FlowModelService flowModelService,
UIContextOperation UIContextOperation,
ISereinIOC sereinIOC,
NodeMVVMService nodeMVVMService)
{
this.flowEnvironment = flowEnvironment;
this.flowEnvironmentEvent = flowEnvironmentEvent;
this.flowLibraryManagement = flowLibraryManagement;
this.flowOperationService = flowOperationService;
this.flowModelService = flowModelService;
this.UIContextOperation = UIContextOperation;
NodeMVVMManagement = nodeMVVMService;
InitNodeMVVM(nodeMVVMService);
}
///
public NodeMVVMService NodeMVVMManagement { get; }
private readonly IFlowEnvironment flowEnvironment;
private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
private readonly FlowLibraryService flowLibraryManagement;
private readonly FlowOperationService flowOperationService;
private readonly FlowModelService flowModelService;
private readonly NodeMVVMService nodeMVVMService;
///
/// 注册基本节点类型
///
private void InitNodeMVVM(NodeMVVMService nodeMVVMService)
{
nodeMVVMService.RegisterModel(NodeControlType.UI, typeof(SingleUINode)); // 动作节点
nodeMVVMService.RegisterModel(NodeControlType.Action, typeof(SingleActionNode)); // 动作节点
nodeMVVMService.RegisterModel(NodeControlType.Flipflop, typeof(SingleFlipflopNode)); // 触发器节点
nodeMVVMService.RegisterModel(NodeControlType.ExpOp, typeof(SingleExpOpNode)); // 表达式节点
nodeMVVMService.RegisterModel(NodeControlType.ExpCondition, typeof(SingleConditionNode)); // 条件表达式节点
nodeMVVMService.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点
nodeMVVMService.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点
nodeMVVMService.RegisterModel(NodeControlType.NetScript, typeof(SingleNetScriptNode)); // 脚本节点
nodeMVVMService.RegisterModel(NodeControlType.FlowCall, typeof(SingleFlowCallNode)); // 流程调用节点
}
private UIContextOperation UIContextOperation;
///
/// 从Guid获取画布
///
/// 节点Guid
/// 节点Model
/// 无法获取节点、Guid/节点为null时报错
public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails)
{
if (string.IsNullOrEmpty(nodeGuid))
{
canvasDetails = null;
return false;
}
return flowModelService.TryGetCanvasModel(nodeGuid, out canvasDetails);
}
///
/// 从Guid获取节点
///
/// 节点Guid
/// 节点Model
/// 无法获取节点、Guid/节点为null时报错
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
{
if (string.IsNullOrEmpty(nodeGuid))
{
nodeModel = null;
return false;
}
return flowModelService.TryGetNodeModel(nodeGuid, out nodeModel);
}
#region 私有方法
///
/// 从节点信息创建节点,并返回状态指示是否创建成功
///
///
///
private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo)
{
if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType))
{
return false;
}
#region 获取方法描述
MethodDetails? methodDetails;
if (controlType == NodeControlType.FlowCall)
{
if (string.IsNullOrEmpty(nodeInfo.MethodName))
{
methodDetails = new MethodDetails();
methodDetails.ParamsArgIndex = 0;
methodDetails.ParameterDetailss = new ParameterDetails[nodeInfo.ParameterData.Length];
for (int i = 0; i < methodDetails.ParameterDetailss.Length; i++)
{
var pdInfo = nodeInfo.ParameterData[i];
var t = new ParameterDetailsInfo();
var pd = new ParameterDetails(pdInfo, i);
methodDetails.ParameterDetailss[i] = pd;
}
}
else
{
// 目标节点可能是方法节点
flowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
}
}
else if (controlType.IsBaseNode())
{
// 加载基础节点
methodDetails = new MethodDetails();
}
else
{
if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false;
// 加载方法节点
flowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
}
#endregion
var nodeModel = FlowNodeExtension.CreateNode(flowEnvironment, controlType, methodDetails); // 加载项目时创建节点
if (nodeModel is null)
{
nodeInfo.Guid = string.Empty;
return false;
}
if (TryGetCanvasModel(nodeInfo.CanvasGuid, out var canvasModel))
{
// 节点与画布互相绑定
// 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常
nodeModel.CanvasDetails = canvasModel;
UIContextOperation?.Invoke(() => canvasModel.Nodes.Add(nodeModel));
nodeModel.LoadInfo(nodeInfo); // 创建节点model
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
}
else
{
SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在");
return false;
}
UIContextOperation?.Invoke(() =>
flowEnvironmentEvent.OnNodeCreated(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上
return true;
}
///
/// 创建节点
///
///
private bool TryAddNode(IFlowNode nodeModel)
{
nodeModel.Guid ??= Guid.NewGuid().ToString();
flowModelService.AddNodeModel(nodeModel);
// 如果是触发器,则需要添加到专属集合中
/*if (nodeModel is SingleFlipflopNode flipflopNode)
{
var guid = flipflopNode.Guid;
if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
{
FlipflopNodes.Add(flipflopNode);
}
}*/
return true;
}
#endregion
#region 流程接口
private int _add_canvas_count = 1;
public void CreateCanvas(string canvasName, int width, int height)
{
IOperation operation = new CreateCanvasOperation
{
CanvasInfo = new FlowCanvasDetailsInfo
{
Name = $"Canvas {_add_canvas_count++}",
Width = width,
Height = height,
Guid = Guid.NewGuid().ToString(),
ScaleX = 1.0f,
ScaleY = 1.0f,
}
};
flowOperationService.Execute(operation);
}
public void RemoveCanvas(string canvasGuid)
{
IOperation operation = new RemoveCanvasOperation
{
CanvasGuid = canvasGuid
};
flowOperationService.Execute(operation);
}
public void ConnectInvokeNode(string canvasGuid, string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionInvokeType invokeType)
{
IOperation operation = new ChangeNodeConnectionOperation
{
CanvasGuid = canvasGuid,
FromNodeGuid = fromNodeGuid,
ToNodeGuid = toNodeGuid,
FromNodeJunctionType = fromNodeJunctionType,
ToNodeJunctionType = toNodeJunctionType,
ConnectionInvokeType = invokeType,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create,
JunctionOfConnectionType = JunctionOfConnectionType.Invoke,
};
flowOperationService.Execute(operation);
}
public void ConnectArgSourceNode(string canvasGuid, string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionArgSourceType argSourceType, int argIndex)
{
IOperation operation = new ChangeNodeConnectionOperation
{
CanvasGuid = canvasGuid,
FromNodeGuid = fromNodeGuid,
ToNodeGuid = toNodeGuid,
FromNodeJunctionType = fromNodeJunctionType,
ToNodeJunctionType = toNodeJunctionType,
ConnectionArgSourceType = argSourceType,
ArgIndex = argIndex,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create,
JunctionOfConnectionType = JunctionOfConnectionType.Arg,
};
flowOperationService.Execute(operation);
}
public void RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
IOperation operation = new ChangeNodeConnectionOperation
{
CanvasGuid = canvasGuid,
FromNodeGuid = fromNodeGuid,
ToNodeGuid = toNodeGuid,
ConnectionInvokeType = connectionType,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove,
};
flowOperationService.Execute(operation);
}
public void RemoveArgSourceConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex)
{
IOperation operation = new ChangeNodeConnectionOperation
{
CanvasGuid = canvasGuid,
FromNodeGuid = fromNodeGuid,
ToNodeGuid = toNodeGuid,
ArgIndex = argIndex,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove
};
flowOperationService.Execute(operation);
}
public void CreateNode(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
{
IOperation operation = new CreateNodeOperation
{
CanvasGuid = canvasGuid,
NodeControlType = nodeType,
Position = position,
MethodDetailsInfo = methodDetailsInfo
};
flowOperationService.Execute(operation);
}
public void RemoveNode(string canvasGuid, string nodeGuid)
{
IOperation operation = new RemoveNodeOperation
{
CanvasGuid = canvasGuid,
NodeGuid = nodeGuid
};
flowOperationService.Execute(operation);
}
public void PlaceNodeToContainer(string canvasGuid, string nodeGuid, string containerNodeGuid)
{
IOperation operation = new ContainerPlaceNodeOperation
{
CanvasGuid = canvasGuid,
NodeGuid = nodeGuid,
ContainerNodeGuid = containerNodeGuid
};
flowOperationService.Execute(operation);
}
public void TakeOutNodeToContainer(string canvasGuid, string nodeGuid)
{
IOperation operation = new ContainerTakeOutNodeOperation
{
CanvasGuid = canvasGuid,
NodeGuid = nodeGuid,
};
flowOperationService.Execute(operation);
}
public void SetStartNode(string canvasGuid, string nodeGuid)
{
if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(nodeGuid, out var newStartNodeModel))
{
return;
}
var oldNodeGuid = canvasModel.StartNode?.Guid;
/*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel))
{
newStartNode.IsStart = false;
}*/
canvasModel.StartNode = newStartNodeModel;
//newStartNode.IsStart = true;
UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnStartNodeChanged(new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, newStartNodeModel.Guid)));
return;
}
public void SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
IOperation operation = new ChangeNodeConnectionOperation
{
CanvasGuid = string.Empty, // 连接优先级不需要画布
FromNodeGuid = fromNodeGuid,
ToNodeGuid = toNodeGuid,
ConnectionInvokeType = connectionType,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create
};
flowOperationService.Execute(operation);
}
public void ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
{
IOperation operation = new ChangeParameterOperation
{
NodeGuid = nodeGuid,
IsAdd = isAdd,
ParamIndex = paramIndex
};
flowOperationService.Execute(operation);
}
///
/// 从节点信息集合批量加载节点控件
///
/// 节点信息
///
///
public async Task LoadNodeInfosAsync(List nodeInfos)
{
#region 从NodeInfo创建NodeModel
// 流程接口节点最后才创建
List flowCallNodeInfos = [];
foreach (NodeInfo? nodeInfo in nodeInfos)
{
if (nodeInfo.Type == nameof(NodeControlType.FlowCall))
{
flowCallNodeInfos.Add(nodeInfo);
}
else
{
if (!CreateNodeFromNodeInfo(nodeInfo))
{
SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}");
continue;
}
}
}
// 创建流程接口节点
foreach (NodeInfo? nodeInfo in flowCallNodeInfos)
{
if (!CreateNodeFromNodeInfo(nodeInfo))
{
SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}");
continue;
}
}
#endregion
#region 重新放置节点
List needPlaceNodeInfos = [];
foreach (NodeInfo? nodeInfo in nodeInfos)
{
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
TryGetNodeModel(nodeInfo.ParentNodeGuid, out var parentNode))
{
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
}
}
foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
{
if (TryGetNodeModel(nodeInfo.Guid, out var nodeModel) &&
TryGetNodeModel(nodeInfo.ParentNodeGuid, out var containerNode)
&& containerNode is INodeContainer nodeContainer)
{
var result = nodeContainer.PlaceNode(nodeModel);
if (result)
{
UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnNodePlace(
new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid)));
}
}
}
#endregion
await Task.Delay(100);
#region 确定节点之间的方法调用关系
foreach (var nodeInfo in nodeInfos)
{
var canvasGuid = nodeInfo.CanvasGuid;
if (!TryGetNodeModel(nodeInfo.Guid, out var fromNodeModel))
{
return;
}
if (fromNodeModel is null) continue;
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
(ConnectionInvokeType.IsError, nodeInfo.ErrorNodes),
(ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)];
foreach ((ConnectionInvokeType connectionType, string[] toNodeGuids) item in allToNodes)
{
// 遍历当前类型分支的节点(确认连接关系)
foreach (var toNodeGuid in item.toNodeGuids)
{
if (!TryGetNodeModel(toNodeGuid, out var toNodeModel))
{
return;
}
if (toNodeModel is null)
{
// 防御性代码,加载正常保存的项目文件不会进入这里
continue;
}
ConnectInvokeNode(canvasGuid, fromNodeModel.Guid, toNodeModel.Guid, JunctionType.NextStep, JunctionType.Execute, item.connectionType);
//var isSuccessful = ConnectInvokeOfNode(canvasGuid, fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系
}
}
//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 nodeInfo)
//{
// // 遍历当前类型分支的节点(确认连接关系)
// foreach (var toNode in item.toNodes)
// {
// _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
// }
//}
}
#endregion
#region 确定节点之间的参数调用关系
var nodeModels = flowModelService.GetAllNodeModel();
foreach (var toNode in nodeModels)
{
var canvasGuid = toNode.CanvasDetails.Guid;
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)
&& TryGetNodeModel(pd.ArgDataSourceNodeGuid, out var fromNode))
{
ConnectArgSourceNode(canvasGuid, fromNode.Guid, toNode.Guid, JunctionType.ReturnData, JunctionType.ArgData, pd.ArgDataSourceType, pd.Index);
}
}
}
#endregion
UIContextOperation?.Invoke(() =>
{
flowEnvironmentEvent.OnProjectLoaded(new ProjectLoadedEventArgs());
});
return;
}
#endregion
#region 视觉效果
///
/// 定位节点
///
///
public void NodeLocate(string nodeGuid)
{
if (OperatingSystem.IsWindows())
{
UIContextOperation?.Invoke(() => flowEnvironmentEvent.OnNodeLocated(new NodeLocatedEventArgs(nodeGuid)));
}
}
#endregion
}
}