From 97df2a04b2e88d4e7dc6c65f746f58c825bed5a0 Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Sun, 22 Jun 2025 21:53:37 +0800
Subject: [PATCH] =?UTF-8?q?LocalFlowEnvironment=E6=96=87=E4=BB=B6=E4=B8=A2?=
=?UTF-8?q?=E5=A4=B1=EF=BC=8C=E9=9C=80=E8=A6=81=E9=87=8D=E5=86=99?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Library/Api/IFlowEnvironment.cs | 92 +-
Library/Api/IFlowNode.cs | 13 +-
Library/Api/ISereinIoc.cs | 22 +-
Library/FlowNode/MethodDetails.cs | 108 +-
...deMVVMManagement.cs => NodeMVVMService.cs} | 2 +-
Library/FlowNode/ParameterDetails.cs | 2 +-
Library/Network/Http/Router.cs | 2 +-
Library/NodeAttribute.cs | 2 +-
Library/NodeStaticConfig.cs | 11 +-
Library/Utils/SereinEnv.cs | 4 +-
Library/Utils/SereinIoc.cs | 179 +-
Net462DllTest/View/FromWorkBenchView.cs | 2 +-
NodeFlow/Env/FlowEnvironment.cs | 93 +-
NodeFlow/Env/FlowEnvironmentEvent.cs | 36 +-
NodeFlow/Env/LocalFlowEnvironment.cs | 3 +
NodeFlow/Env/LocalFlowEnvironment_T.cs | 2170 +++++++++++++++++
NodeFlow/Env/RemoteFlowEnvironment.cs | 9 +-
NodeFlow/FlowNodeExtension.cs | 2 +-
.../Model/{ => Node}/NodeModelBaseData.cs | 13 +
.../Model/{ => Node}/NodeModelBaseFunc.cs | 4 +-
NodeFlow/Model/{ => Node}/SingleActionNode.cs | 2 +-
.../Model/{ => Node}/SingleConditionNode.cs | 0
NodeFlow/Model/{ => Node}/SingleExpOpNode.cs | 0
.../Model/{ => Node}/SingleFlipflopNode.cs | 10 +-
.../Model/{ => Node}/SingleFlowCallNode.cs | 7 +-
.../Model/{ => Node}/SingleGlobalDataNode.cs | 6 +-
.../Model/{ => Node}/SingleNetScriptNode.cs | 0
NodeFlow/Model/{ => Node}/SingleScriptNode.cs | 2 +-
NodeFlow/Model/{ => Node}/SingleUINode.cs | 4 +-
.../ChangeNodeConnectionOperation.cs | 451 ++++
.../Operation/ChangeParameterOperation.cs | 90 +
.../Operation/ContainerPlaceNodeOperation.cs | 98 +
.../ContainerTakeOutNodeOperation.cs | 89 +
.../Model/Operation/CreateCanvasOperation.cs | 51 +
.../Model/Operation/CreateNodeOperation.cs | 145 ++
NodeFlow/Model/Operation/OperationBase.cs | 126 +-
.../Model/Operation/RemoveCanvasOperation.cs | 59 +
.../Model/Operation/RemoveNodeOperation.cs | 216 ++
.../SetConnectPriorityInvokeOperation.cs | 91 +
.../Model/Operation/SetStartNodeOperation.cs | 78 +
NodeFlow/Serein.NodeFlow.csproj | 6 +
NodeFlow/Services/FlowModelService.cs | 99 +
NodeFlow/Services/FlowOperationService.cs | 81 +
NodeFlow/{ => Services}/FlowWorkManagement.cs | 8 +-
NodeFlow/Services/NodeService.cs | 12 -
.../Customs/FlowMethodInfoListBox.xaml.cs | 2 +-
.../Node/Junction/JunctionControlBase.cs | 20 +-
Workbench/Node/View/ConnectionControl.cs | 4 +-
.../ViewModel/ActionNodeControlViewModel.cs | 2 +-
.../ViewModel/FlipflopNodeControlViewModel.cs | 2 +-
.../Node/ViewModel/UINodeControlViewModel.cs | 2 +-
Workbench/Services/FlowEEForwardingService.cs | 11 +-
Workbench/Services/FlowNodeService.cs | 12 +-
Workbench/Services/FlowProjectService.cs | 5 +
Workbench/Themes/MethodDetailsControl.xaml.cs | 2 +-
Workbench/ViewModels/MainMenuBarViewModel.cs | 48 +-
Workbench/Views/FlowCanvasView.xaml.cs | 15 +-
Workbench/Views/MainMenuBarView.xaml | 14 +-
58 files changed, 4285 insertions(+), 354 deletions(-)
rename Library/FlowNode/{NodeMVVMManagement.cs => NodeMVVMService.cs} (98%)
create mode 100644 NodeFlow/Env/LocalFlowEnvironment_T.cs
rename NodeFlow/Model/{ => Node}/NodeModelBaseData.cs (89%)
rename NodeFlow/Model/{ => Node}/NodeModelBaseFunc.cs (99%)
rename NodeFlow/Model/{ => Node}/SingleActionNode.cs (90%)
rename NodeFlow/Model/{ => Node}/SingleConditionNode.cs (100%)
rename NodeFlow/Model/{ => Node}/SingleExpOpNode.cs (100%)
rename NodeFlow/Model/{ => Node}/SingleFlipflopNode.cs (85%)
rename NodeFlow/Model/{ => Node}/SingleFlowCallNode.cs (98%)
rename NodeFlow/Model/{ => Node}/SingleGlobalDataNode.cs (97%)
rename NodeFlow/Model/{ => Node}/SingleNetScriptNode.cs (100%)
rename NodeFlow/Model/{ => Node}/SingleScriptNode.cs (99%)
rename NodeFlow/Model/{ => Node}/SingleUINode.cs (94%)
create mode 100644 NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
create mode 100644 NodeFlow/Model/Operation/ChangeParameterOperation.cs
create mode 100644 NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs
create mode 100644 NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs
create mode 100644 NodeFlow/Model/Operation/CreateCanvasOperation.cs
create mode 100644 NodeFlow/Model/Operation/CreateNodeOperation.cs
create mode 100644 NodeFlow/Model/Operation/RemoveCanvasOperation.cs
create mode 100644 NodeFlow/Model/Operation/RemoveNodeOperation.cs
create mode 100644 NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs
create mode 100644 NodeFlow/Model/Operation/SetStartNodeOperation.cs
create mode 100644 NodeFlow/Services/FlowModelService.cs
create mode 100644 NodeFlow/Services/FlowOperationService.cs
rename NodeFlow/{ => Services}/FlowWorkManagement.cs (98%)
delete mode 100644 NodeFlow/Services/NodeService.cs
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index 4e5cd93..40c485d 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -260,8 +260,8 @@ namespace Serein.Library.Api
public NodeConnectChangeEventArgs(string canvasGuid,
string fromNodeGuid,
string toNodeGuid,
- JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
int argIndex,
+ JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
ConnectionArgSourceType connectionArgSourceType, // 节点对应的方法入参所需参数来源
ConnectChangeType changeType) // 需要创建连接线还是删除连接线
{
@@ -296,15 +296,15 @@ namespace Serein.Library.Api
///
/// 指示需要创建什么类型的连接线
///
- public JunctionOfConnectionType JunctionOfConnectionType { get;}
+ public JunctionOfConnectionType JunctionOfConnectionType { get; } = JunctionOfConnectionType.None;
///
/// 节点对应的方法入参所需参数来源
///
- public ConnectionArgSourceType ConnectionArgSourceType { get;}
+ public ConnectionArgSourceType ConnectionArgSourceType { get;}
///
/// 第几个参数
///
- public int ArgIndex { get;}
+ public int ArgIndex { get; } = -1;
}
@@ -814,7 +814,7 @@ namespace Serein.Library.Api
///
/// 节点视图模型管理类
///
- NodeMVVMManagement NodeMVVMManagement { get; }
+ NodeMVVMService NodeMVVMManagement { get; }
#endregion
#region 基本接口
@@ -904,23 +904,17 @@ namespace Serein.Library.Api
/// 宽度
/// 高度
///
- Task CreateCanvasAsync(string canvasName, int width , int height);
+ void CreateCanvas(string canvasName, int width , int height);
///
/// 删除画布
///
/// 画布Guid
///
- Task RemoveCanvasAsync(string canvasGuid);
+ void RemoveCanvas(string canvasGuid);
+
- ///
- /// 设置流程起点节点
- ///
- /// 所在画布
- /// 尝试设置为起始节点的节点Guid
- /// 被设置为起始节点的Guid
- Task SetStartNodeAsync(string canvasGuid, string nodeGuid);
///
/// 在两个节点之间创建连接关系
@@ -931,7 +925,7 @@ namespace Serein.Library.Api
/// 起始节点控制点
/// 目标节点控制点
/// 决定了方法执行后的后继行为
- Task ConnectInvokeNodeAsync(string canvasGuid,
+ void ConnectInvokeNode(string canvasGuid,
string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
@@ -948,7 +942,7 @@ namespace Serein.Library.Api
/// 目标节点控制点
/// 决定了方法参数来源
/// 设置第几个参数
- Task ConnectArgSourceNodeAsync(string canvasGuid,
+ void ConnectArgSourceNode(string canvasGuid,
string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
@@ -956,6 +950,23 @@ namespace Serein.Library.Api
ConnectionArgSourceType argSourceType,
int argIndex);
+ ///
+ /// 移除两个节点之间的方法调用关系
+ ///
+ /// 所在画布
+ /// 起始节点
+ /// 目标节点
+ /// 连接类型
+ void RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
+
+ ///
+ /// 移除连接节点之间参数传递的关系
+ ///
+ /// 所在画布
+ /// 起始节点Guid
+ /// 目标节点Guid
+ /// 连接到第几个参数
+ void RemoveArgSourceConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex);
///
@@ -965,8 +976,14 @@ namespace Serein.Library.Api
/// 控件类型
/// 节点在画布上的位置(
/// 节点绑定的方法说明
- Task CreateNodeAsync(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
+ void CreateNode(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
+ ///
+ /// 移除节点
+ ///
+ /// 所在画布
+ /// 待移除的节点Guid
+ void RemoveNode(string canvasGuid, string nodeGuid);
///
/// 将节点放置在容器中
@@ -975,15 +992,22 @@ namespace Serein.Library.Api
/// 需要放置的节点Guid
/// 存放节点的容器Guid
///
- Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid);
+ void PlaceNodeToContainer(string canvasGuid, string nodeGuid, string containerNodeGuid);
///
/// 将节点放置在容器中
///
/// 所在画布
/// 需要取出的节点Guid
- Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid);
+ void TakeOutNodeToContainer(string canvasGuid, string nodeGuid);
+ ///
+ /// 设置流程起点节点
+ ///
+ /// 所在画布
+ /// 尝试设置为起始节点的节点Guid
+ /// 被设置为起始节点的Guid
+ void SetStartNode(string canvasGuid, string nodeGuid);
///
/// 设置两个节点某个类型的方法调用关系为优先调用
@@ -992,32 +1016,8 @@ namespace Serein.Library.Api
/// 目标节点
/// 连接关系
///
- Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
+ void SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
- ///
- /// 移除两个节点之间的方法调用关系
- ///
- /// 所在画布
- /// 起始节点
- /// 目标节点
- /// 连接类型
- Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
-
- ///
- /// 移除连接节点之间参数传递的关系
- ///
- /// 所在画布
- /// 起始节点Guid
- /// 目标节点Guid
- /// 连接到第几个参数
- Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex);
-
- ///
- /// 移除节点/区域/基础控件
- ///
- /// 所在画布
- /// 待移除的节点Guid
- Task RemoveNodeAsync(string canvasGuid, string nodeGuid);
///
/// 改变可选参数的数目
@@ -1026,8 +1026,7 @@ namespace Serein.Library.Api
/// true,增加参数;false,减少参数
/// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)
///
- Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex);
-
+ void ChangeParameter(string nodeGuid, bool isAdd, int paramIndex);
#endregion
@@ -1114,6 +1113,7 @@ namespace Serein.Library.Api
/// 需要你提供一个由你实现的ISereinIOC接口实现类
/// 当你将流程运行环境集成在你的项目时,并希望流程运行时使用你提供的对象,而非自动创建
/// 就需要你调用这个方法,用来替换运行环境的IOC容器
+ /// 注意,是流程运行时,而非运行环境
///
///
void UseExternalIOC(ISereinIOC ioc);
diff --git a/Library/Api/IFlowNode.cs b/Library/Api/IFlowNode.cs
index 0d4f829..be7127b 100644
--- a/Library/Api/IFlowNode.cs
+++ b/Library/Api/IFlowNode.cs
@@ -72,14 +72,19 @@ namespace Serein.Library.Api
MethodDetails MethodDetails { get; set; }
///
- /// 父节点
+ /// 父节点集合
///
Dictionary> PreviousNodes { get;}
///
- /// 子节点
+ /// 子节点集合
///
Dictionary> SuccessorNodes { get; set; }
+ ///
+ /// 需要该节点返回结果作为入参参数的节点集合
+ ///
+ Dictionary> NeedResultNodes { get; }
+
///
/// 当该节点放置在某个具有容器行为的节点时,该值指示其容器节点
///
@@ -94,10 +99,10 @@ namespace Serein.Library.Api
/// 节点创建时的行为
///
void OnCreating();
- ///
+ /*///
/// 节点移除时的行为
///
- void Remove();
+ void Remove();*/
///
/// 节点保存时如若需要保存自定义数据,可通过该方法进行控制保存逻辑
diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs
index 401c0f4..2aade37 100644
--- a/Library/Api/ISereinIoc.cs
+++ b/Library/Api/ISereinIoc.cs
@@ -18,14 +18,14 @@ namespace Serein.Library.Api
ISereinIOC Reset();
///
- /// 通过指定类型的方式注册实例
+ /// 通过指定类型的方式注册实例,该类型的实例由你提供
///
/// 实例类型
///
ISereinIOC Register(Type type);
///
- /// 通过指定类型的方式注册实例
+ /// 通过指定类型的方式注册实例,该类型将由IOC容器自动创建
///
/// 实例类型
/// 获取实例的回调函数
@@ -33,14 +33,14 @@ namespace Serein.Library.Api
ISereinIOC Register(Type type, Func
- object CreateTempObject(Type type);
+ object CreateObject(Type type);
///
/// 给定一个类型,由IOC容器负责创建实例,如果存在多个构造函数,将由参数最多的构造函数开始尝试创建。
///
///
- T CreateTempObject();
+ T CreateObject();
+
+ ///
+ /// 给定一个实例,尽可能地在该实例中具有[AutoInjection]特性的属性上,设置为IOC容器中已有的对应类型的对象。
+ ///
+ /// 对应的类型
+ /// 传入的实例
+ ///
+ T InjectDependenciesProperty(T instance);
///
/// 搜寻已注册的类型生成依赖关系,依次实例化并注入依赖项,缓存在由IOC容器维护的Map中,直到手动调用Reset()方法。
diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs
index 8aefd42..1b380c2 100644
--- a/Library/FlowNode/MethodDetails.cs
+++ b/Library/FlowNode/MethodDetails.cs
@@ -103,51 +103,101 @@ namespace Serein.Library
/// 新增可变参数
///
///
- public bool AddParamsArg(int index = 0)
+ public bool AddParamsArg(int index)
{
- if (ParamsArgIndex >= 0 // 方法是否包含可变参数
- && index >= 0 // 如果包含,则判断从哪个参数赋值
- && index >= ParamsArgIndex // 需要判断是否为可选参数的部分
- && index < ParameterDetailss.Length) // 防止下标越界
- {
- var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
- newPd.Index = ParameterDetailss.Length; // 更新索引
- newPd.IsParams = true;
- ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, newPd); // 新增
- return true;
- }
- else
+ if (ParamsArgIndex < 0 // 方法是否包含可变参数
+ || index < 0 // 如果包含,则判断从哪个参数赋值
+ || index < ParamsArgIndex // 需要判断是否为可选参数的部分
+ || index >= ParameterDetailss.Length) // 防止下标越界
{
return false;
}
+
+ var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
+ newPd.Index = ParameterDetailss.Length; // 更新索引
+ newPd.IsParams = true;
+ ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, newPd); // 新增
+ return true;
}
+
+
+ public bool AddParamsArg(ParameterDetails parameterDetails)
+ {
+ if (ParamsArgIndex < 0) // 方法是否包含可变参数
+ {
+ return false;
+ }
+ if (parameterDetails is null)
+ {
+ return false;
+ }
+ parameterDetails.Index = ParameterDetailss.Length; // 更新索引
+ parameterDetails.IsParams = true;
+ ParameterDetailss = ArrayHelper.AddToArray(ParameterDetailss, parameterDetails); // 新增
+ return true;
+
+ }
+
+
///
/// 移除可变参数
///
///
- public bool RemoveParamsArg(int index = 0)
+ public bool RemoveParamsArg(int index)
{
- if (ParamsArgIndex >= 0 // 方法是否包含可变参数
- && index >= 0 // 如果包含,则判断从哪个参数赋值
- && index > ParamsArgIndex // 需要判断是否为可选参数的部分,并且不能删除原始的可变参数描述
- && index < ParameterDetailss.Length) // 防止下标越界
- {
- ParameterDetailss[index] = null; // 释放对象引用
-
-
- var tmp = ArrayHelper.RemoteToArray(ParameterDetailss, index); // 新增;
- UpdateParamIndex(ref tmp);
- ParameterDetailss = tmp; // 新增
- return true;
- }
- else
+ if (ParamsArgIndex < 0 // 方法是否包含可变参数
+ || index < 0 // 如果包含,则判断从哪个参数赋值
+ || index <= ParamsArgIndex // 需要判断是否为可选参数的部分,并且不能删除原始的可变参数描述
+ || index >= ParameterDetailss.Length) // 防止下标越界
{
return false;
}
-
+
+ ParameterDetailss[index] = null; // 释放对象引用
+ var tmp = ArrayHelper.RemoteToArray(ParameterDetailss, index); // 新增;
+ UpdateParamIndex(ref tmp);
+ ParameterDetailss = tmp; // 新增
+ return true;
}
+ ///
+ /// 移除可变参数
+ ///
+ ///
+ public bool RemoveParamsArg(ParameterDetails parameterDetails)
+ {
+ if (ParamsArgIndex < 0) // 方法是否包含可变参数
+ {
+ return false;
+ }
+ if (parameterDetails is null)
+ {
+ return false;
+ }
+ int index = -1;
+ for (int i = 0; i < ParameterDetailss.Length; i++)
+ {
+ var pd = ParameterDetailss[i];
+ if (pd.Equals(parameterDetails))
+ {
+ index = i;
+ break;
+ }
+ }
+ if (index == -1)
+ {
+ return false;
+ }
+
+ ParameterDetailss[index] = null; // 释放对象引用
+ var tmp = ArrayHelper.RemoteToArray(ParameterDetailss, index); // 新增;
+ UpdateParamIndex(ref tmp);
+ ParameterDetailss = tmp; // 新增
+ return true;
+ }
+
+
///
/// 更新参数的索引
///
diff --git a/Library/FlowNode/NodeMVVMManagement.cs b/Library/FlowNode/NodeMVVMService.cs
similarity index 98%
rename from Library/FlowNode/NodeMVVMManagement.cs
rename to Library/FlowNode/NodeMVVMService.cs
index ee7def3..a40d1f7 100644
--- a/Library/FlowNode/NodeMVVMManagement.cs
+++ b/Library/FlowNode/NodeMVVMService.cs
@@ -45,7 +45,7 @@ namespace Serein.Library
///
/// 节点 数据、视图、VM 管理
///
- public class NodeMVVMManagement
+ public class NodeMVVMService
{
///
/// 节点对应的控件类型
diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs
index 097ae0d..b7c732b 100644
--- a/Library/FlowNode/ParameterDetails.cs
+++ b/Library/FlowNode/ParameterDetails.cs
@@ -246,7 +246,7 @@ namespace Serein.Library
var type = EnumHelper.GetBoundValue(ExplicitType, resultEnum, attr => attr.Value);
if (type is Type enumBindType && !(enumBindType is null))
{
- var value = nodeModel.Env.IOC.CreateTempObject(enumBindType);
+ var value = nodeModel.Env.IOC.CreateObject(enumBindType);
return value;
}
}
diff --git a/Library/Network/Http/Router.cs b/Library/Network/Http/Router.cs
index 6538203..c7c4ce8 100644
--- a/Library/Network/Http/Router.cs
+++ b/Library/Network/Http/Router.cs
@@ -145,7 +145,7 @@ namespace Serein.Library.Web
return false; // 没有对应的处理配置
}
- ControllerBase controllerInstance = (ControllerBase)SereinIOC.CreateTempObject(controllerType);
+ ControllerBase controllerInstance = (ControllerBase)SereinIOC.CreateObject(controllerType);
if (controllerInstance is null)
{
diff --git a/Library/NodeAttribute.cs b/Library/NodeAttribute.cs
index 68da2fd..fcaf0df 100644
--- a/Library/NodeAttribute.cs
+++ b/Library/NodeAttribute.cs
@@ -9,7 +9,7 @@ namespace Serein.Library
/// 这种情况会导致流程启动时,IOC容器无法注入构造函数并创建类型,导致启动失败。
/// 解决方法:从ServiceA类的构造函数中移除ServiceB类型的入参,将该类型更改为公开可见的可写属性成员ServiceB serviceB{get;set;},并在该属性上标记[AutoInjection]特性
///
- [AttributeUsage(AttributeTargets.Property)]
+ [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)]
public sealed class AutoInjectionAttribute : Attribute
{
}
diff --git a/Library/NodeStaticConfig.cs b/Library/NodeStaticConfig.cs
index 6931234..c3b8ff2 100644
--- a/Library/NodeStaticConfig.cs
+++ b/Library/NodeStaticConfig.cs
@@ -21,10 +21,19 @@ namespace Serein.Library
///
public static readonly ConnectionInvokeType[] ConnectionTypes = new ConnectionInvokeType[]
{
- ConnectionInvokeType.Upstream,
+ ConnectionInvokeType.Upstream,
ConnectionInvokeType.IsSucceed,
ConnectionInvokeType.IsFail,
ConnectionInvokeType.IsError,
};
+ ///
+ /// 节点连接关系种类
+ ///
+ public static readonly ConnectionArgSourceType[] ConnectionArgSourceTypes = new ConnectionArgSourceType[]
+ {
+ ConnectionArgSourceType.GetPreviousNodeData,
+ ConnectionArgSourceType.GetOtherNodeData,
+ ConnectionArgSourceType.GetOtherNodeDataOfInvoke,
+ };
}
}
diff --git a/Library/Utils/SereinEnv.cs b/Library/Utils/SereinEnv.cs
index 8ecc03c..bcecb5d 100644
--- a/Library/Utils/SereinEnv.cs
+++ b/Library/Utils/SereinEnv.cs
@@ -3,6 +3,7 @@ using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data;
+using System.Diagnostics;
using System.Diagnostics.Contracts;
using System.Linq;
using System.Text;
@@ -122,7 +123,8 @@ namespace Serein.Library
/// 级别
public static void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.General)
{
- SereinEnv.environment.WriteLine(type,message,@class);
+ Debug.WriteLine($"{type} : {message}");
+ SereinEnv.environment?.WriteLine(type,message,@class);
}
///
diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs
index 3fa7b45..84b062f 100644
--- a/Library/Utils/SereinIoc.cs
+++ b/Library/Utils/SereinIoc.cs
@@ -137,13 +137,13 @@ namespace Serein.Library.Utils
#endregion
- #region 示例的获取
+ #region 示例的创建
///
/// 用于临时实例的创建,不登记到IOC容器中,依赖项注入失败时也不记录。
///
///
///
- public object CreateTempObject(Type type)
+ public object CreateObject(Type type)
{
var ctors = GetConstructor(type); // 获取构造函数
object instance = null;
@@ -180,10 +180,55 @@ namespace Serein.Library.Utils
///
///
///
- public T CreateTempObject()
+ public T CreateObject()
{
- return (T)CreateTempObject(typeof(T));
- }
+ return (T)CreateObject(typeof(T));
+ }
+
+
+ ///
+ /// 给定一个实例,尽可能地在该实例中具有[AutoInjection]特性的属性/字段上,设置为IOC容器中已有的对应类型的对象。
+ ///
+ ///
+ ///
+ ///
+ public T InjectDependenciesProperty(T instance)
+ {
+ var type = instance.GetType();
+ var properties = type.GetType()
+ .GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic).ToArray()
+ .Where(p => p.CanWrite // 可写属性
+ && p.GetCustomAttribute() != null // 有特性标注需要注入
+ && p.GetValue(instance) == null); // 属性为空
+
+ // 属性注入
+ foreach (var property in properties)
+ {
+ var propertyType = property.PropertyType;
+ if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
+ {
+ property.SetValue(instance, dependencyInstance); // 尝试写入到目标实例的属性中
+ }
+ }
+
+
+ // 字段注入
+ var fields = type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic )
+ .Where(f => f.GetCustomAttribute() != null
+ && f.GetValue(instance) == null);
+
+ foreach (var field in fields)
+ {
+ var fieldType = field.FieldType;
+ if (_dependencies.TryGetValue(fieldType.FullName, out var dependencyInstance))
+ {
+ field.SetValue(instance, dependencyInstance);
+ }
+ }
+
+ return instance;
+ }
+
#endregion
#region 通过名称记录或获取一个实例
@@ -257,56 +302,62 @@ namespace Serein.Library.Utils
public string Name { get; set; }
public Type Type { get; set; }
}
- private const string FlowBaseClassName = "@LibraryRootNode";
+
+
+ private const string IOC_MAIN = "*Priority Instantiation*";
///
- /// 构建依赖关系树
+ /// 遍历所有需要注册的类型,获取到它们所有构造函数,并统计每个构造函数的入参类型,构建依赖关系树
///
- ///
+ /// “ID-PID”关系的树形结构
private Dictionary> BuildDependencyTree()
{
var dependencyMap = new Dictionary>();
- dependencyMap[FlowBaseClassName] = new HashSet();
+ dependencyMap[IOC_MAIN] = new HashSet(); // 优先实例化
foreach (var typeMapping in _typeMappings)
{
- var constructors = GetConstructor(typeMapping.Value); // 获取构造函数
+ var typeFullName = typeMapping.Key; // 注册的类型 FullName
+ var type = typeMapping.Value; // 对应的Type。如果是以接口形式注册,typeFullName将是接口类的FullName,而type将是接口实现类。
+ var constructors = GetConstructor(type); // 获取构造函数
+
foreach (var constructor in constructors)
{
- if (constructor != null)
+ if (constructor is null)
{
- var parameters = constructor.GetParameters()
- .Select(p => p.ParameterType)
- .ToList();
- if (parameters.Count == 0) // 无参的构造函数
+ continue;
+ }
+ var parameters = constructor.GetParameters().Select(p => p.ParameterType);
+ var ctorCount = constructors.Length;
+ var ctorParamCount = parameters.Count();
+
+ // 类型仅有一个构造函数,并且无参,将优先实例化
+ if (ctorCount == 1 && ctorParamCount == 0)
+ {
+ if (!dependencyMap[IOC_MAIN].Contains(type.FullName))
{
- var type = typeMapping.Value;
- if (!dependencyMap[FlowBaseClassName].Contains(type.FullName))
- {
- dependencyMap[FlowBaseClassName].Add(type.FullName);
- }
+ dependencyMap[IOC_MAIN].Add(type.FullName);
+ }
+ continue;
+ }
+
+ // 从类型的有参构造函数中提取类型
+ foreach (var param in parameters)
+ {
+ if (!dependencyMap.TryGetValue(param.FullName, out var hashSet))
+ {
+ hashSet = new HashSet();
+ hashSet.Add(typeMapping.Key);
+ dependencyMap.Add(param.FullName, hashSet);
}
else
{
- // 从类型的有参构造函数中提取类型
- foreach (var param in parameters)
+ if (!hashSet.Contains(typeMapping.Key))
{
- if (!dependencyMap.TryGetValue(param.FullName, out var hashSet))
- {
- hashSet = new HashSet();
- hashSet.Add(typeMapping.Key);
- dependencyMap.Add(param.FullName, hashSet);
- }
- else
- {
- if (!hashSet.Contains(typeMapping.Key))
- {
- hashSet.Add(typeMapping.Key);
- }
- }
-
+ hashSet.Add(typeMapping.Key);
}
}
}
+
}
}
@@ -315,7 +366,7 @@ namespace Serein.Library.Utils
}
///
- /// 获取类型的获取所有构造函数
+ /// 获取类型的所有构造函数,根据入参数量,由多到少排列
///
///
///
@@ -325,42 +376,46 @@ namespace Serein.Library.Utils
}
///
- /// 创建示例的生成顺序
+ /// 创建实例的生成顺序
///
- ///
+ /// 依赖关系树
///
public List GetCreationOrder(Dictionary> dependencyMap)
{
- var graph = new Dictionary>();
- var indegree = new Dictionary();
+ var graph = new Dictionary>(); // 另一种依赖关系树
+ var indegree = new Dictionary(); // 表示出现次数
foreach (var entry in dependencyMap)
{
- var key = entry.Key;
- if (!graph.ContainsKey(key))
+
+ // “rootNode”是注册类的类型FullName属性
+ var rootNode = entry.Key; // 根节点
+
+ if (!graph.ContainsKey(rootNode))
{
- graph[key] = new List();
+ graph[rootNode] = new List();
}
- foreach (var dependent in entry.Value)
+ // “childNode”是注册类构造函数中出现过的参数的类型FullName属性
+ foreach (var childNode in entry.Value)
{
- if (!graph.ContainsKey(dependent))
+ if (!graph.ContainsKey(childNode))
{
- graph[dependent] = new List();
+ graph[childNode] = new List();
}
- graph[key].Add(dependent);
+ graph[rootNode].Add(childNode);
// 更新入度
- if (!indegree.ContainsKey(dependent))
+ if (!indegree.ContainsKey(childNode))
{
- indegree[dependent] = 0;
+ indegree[childNode] = 0;
}
- indegree[dependent]++;
+ indegree[childNode]++;
}
- if (!indegree.ContainsKey(key))
+ if (!indegree.ContainsKey(rootNode))
{
- indegree[key] = 0;
+ indegree[rootNode] = 0;
}
}
@@ -386,10 +441,10 @@ namespace Serein.Library.Utils
if (tmpList.Count > 0)
{
StringBuilder sb = new StringBuilder();
- sb.Append("以下类型可能产生循环依赖,请避免循环依赖,如果确实需要循环引用,请使用 [AutoInjection] 特性注入属性");
+ sb.Append("以下类型存在循环依赖,请避免循环依赖,如果确实需要循环引用,请使用 [AutoInjection] 特性注入属性");
foreach (var kv in tmpList)
{
- sb.AppendLine($"Class Name : {kv}");
+ sb.AppendLine($"类名 : {kv}");
}
SereinEnv.WriteLine(InfoType.ERROR, sb.ToString());
}
@@ -412,9 +467,9 @@ namespace Serein.Library.Utils
{
return instance;
}
- if (_registerCallback.TryGetValue(typeName,out var obj))
+ if (_registerCallback.TryGetValue(typeName,out var getInstance))
{
- instance = obj;
+ return getInstance.Invoke();
}
// 字符串、值类型,抽象类型,暂时不支持自动创建
@@ -427,7 +482,7 @@ namespace Serein.Library.Utils
{
// 没有显示指定构造函数入参,选择参数最多的构造函数
//var constructor = GetConstructorWithMostParameters(type);
- var constructors = GetConstructor(type); // 获取参数最多的构造函数
+ var constructors = GetConstructor(type); // 获取构造函数
foreach(var constructor in constructors)
{
@@ -480,15 +535,16 @@ namespace Serein.Library.Utils
///
public ISereinIOC Build()
{
- var dependencyTree = BuildDependencyTree();
- var creationOrder = GetCreationOrder(dependencyTree);
+ var dependencyTree = BuildDependencyTree(); // 生成类型依赖关系
+ var creationOrder = GetCreationOrder(dependencyTree); // 生成创建顺序
// 输出创建顺序
- Debug.WriteLine("创建顺序: " + string.Join(" → ", creationOrder));
+ Debug.WriteLine("创建顺序: " + string.Join($"{Environment.NewLine}↓ {Environment.NewLine}", creationOrder));
// 创建对象
foreach (var typeName in creationOrder)
{
+
if (_dependencies.ContainsKey(typeName))
{
continue;
@@ -496,6 +552,7 @@ namespace Serein.Library.Utils
var value = CreateInstance(typeName);
if(value is null)
{
+ SereinEnv.WriteLine(InfoType.ERROR, $"IOC容器无法创建对象:{typeName}");
continue;
}
_dependencies[typeName] = value;
diff --git a/Net462DllTest/View/FromWorkBenchView.cs b/Net462DllTest/View/FromWorkBenchView.cs
index 60a51c5..d5d9c1e 100644
--- a/Net462DllTest/View/FromWorkBenchView.cs
+++ b/Net462DllTest/View/FromWorkBenchView.cs
@@ -21,7 +21,7 @@ namespace Net462DllTest
if (ViewModel is null)
{
SereinEnv.WriteLine(InfoType.INFO, "创建对象并注入依赖项");
- ViewModel = env.IOC.CreateTempObject();
+ ViewModel = env.IOC.CreateObject();
}
BindData();
}
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index 1f6b34b..5efc858 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -2,6 +2,7 @@
using Serein.Library.Api;
using Serein.Library.FlowNode;
using Serein.Library.Utils;
+using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System.Reflection;
@@ -14,15 +15,26 @@ namespace Serein.NodeFlow.Env
{
public FlowEnvironment()
{
- flowEnvironmentEvent = new FlowEnvironmentEvent();
- flowEnvironment = new LocalFlowEnvironment(flowEnvironmentEvent);
+ ISereinIOC sereinIOC = new SereinIOC();
+ sereinIOC.Reset();
+ sereinIOC.Register(()=> sereinIOC); // 注册IOC
+ sereinIOC.Register(() => this);
+ sereinIOC.Register();
+ sereinIOC.Register();
+ sereinIOC.Register();
+ sereinIOC.Register();
+ sereinIOC.Register();
+ sereinIOC.Register();
+ sereinIOC.Build();
+ this.IOC = sereinIOC;
+
// 默认使用本地环境
- currentFlowEnvironment = flowEnvironment;
- currentFlowEnvironmentEvent = flowEnvironmentEvent;
+ currentFlowEnvironment = sereinIOC.Get();
+ currentFlowEnvironmentEvent = sereinIOC.Get();
SereinEnv.SetEnv(currentFlowEnvironment);
}
- ///
+ /* ///
/// 本地环境
///
private readonly LocalFlowEnvironment flowEnvironment;
@@ -30,7 +42,7 @@ namespace Serein.NodeFlow.Env
///
/// 远程环境
///
- private RemoteFlowEnvironment remoteFlowEnvironment;
+ private RemoteFlowEnvironment remoteFlowEnvironment;*/
///
/// 本地环境事件
@@ -79,9 +91,10 @@ namespace Serein.NodeFlow.Env
///
public UIContextOperation UIContextOperation => currentFlowEnvironment.UIContextOperation;
///
- public NodeMVVMManagement NodeMVVMManagement => currentFlowEnvironment.NodeMVVMManagement;
+ public NodeMVVMService NodeMVVMManagement => currentFlowEnvironment.NodeMVVMManagement;
+
///
- public ISereinIOC IOC => currentFlowEnvironment.IOC;
+ public ISereinIOC IOC { get; set; }
///
public IFlowEnvironmentEvent Event => currentFlowEnvironment.Event;
@@ -239,30 +252,30 @@ namespace Serein.NodeFlow.Env
}
///
- public async Task CreateCanvasAsync(string canvasName, int width, int height)
+ public void CreateCanvas(string canvasName, int width, int height)
{
- return await currentFlowEnvironment.CreateCanvasAsync(canvasName, width, height);
+ currentFlowEnvironment.CreateCanvas(canvasName, width, height);
}
///
- public async Task RemoveCanvasAsync(string canvasGuid)
+ public void RemoveCanvas(string canvasGuid)
{
- return await currentFlowEnvironment.RemoveCanvasAsync(canvasGuid);
+ currentFlowEnvironment.RemoveCanvas(canvasGuid);
}
///
- public async Task ConnectInvokeNodeAsync(string canvasGuid,
+ public void ConnectInvokeNode(string canvasGuid,
string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType,
ConnectionInvokeType invokeType)
{
- return await currentFlowEnvironment.ConnectInvokeNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType);
+ currentFlowEnvironment.ConnectInvokeNode(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType);
}
///
- public async Task ConnectArgSourceNodeAsync(string canvasGuid,
+ public void ConnectArgSourceNode(string canvasGuid,
string fromNodeGuid,
string toNodeGuid,
JunctionType fromNodeJunctionType,
@@ -270,7 +283,7 @@ namespace Serein.NodeFlow.Env
ConnectionArgSourceType argSourceType,
int argIndex)
{
- return await currentFlowEnvironment.ConnectArgSourceNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex);
+ currentFlowEnvironment.ConnectArgSourceNode(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex);
}
///
@@ -281,8 +294,8 @@ namespace Serein.NodeFlow.Env
if (isConnect)
{
- remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteMsgUtil, this.Event, this.UIContextOperation);
- currentFlowEnvironment = remoteFlowEnvironment;
+ /* remoteFlowEnvironment ??= new RemoteFlowEnvironment(remoteMsgUtil, this.Event, this.UIContextOperation);
+ currentFlowEnvironment = remoteFlowEnvironment;*/
}
return (isConnect, remoteMsgUtil);
}
@@ -296,30 +309,27 @@ namespace Serein.NodeFlow.Env
}
///
- public async Task CreateNodeAsync(string canvasGuid, NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
+ public void CreateNode(string canvasGuid, NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
{
SetProjectLoadingFlag(false);
- var result = await currentFlowEnvironment.CreateNodeAsync(canvasGuid, nodeBase, position, methodDetailsInfo); // 装饰器调用
+ currentFlowEnvironment.CreateNode(canvasGuid, nodeBase, position, methodDetailsInfo); // 装饰器调用
SetProjectLoadingFlag(true);
- return result;
}
///
- public async Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid)
+ public void PlaceNodeToContainer(string canvasGuid, string nodeGuid, string containerNodeGuid)
{
SetProjectLoadingFlag(false);
- var result = await currentFlowEnvironment.PlaceNodeToContainerAsync(canvasGuid, nodeGuid, containerNodeGuid); // 装饰器调用
+ currentFlowEnvironment.PlaceNodeToContainer(canvasGuid, nodeGuid, containerNodeGuid); // 装饰器调用
SetProjectLoadingFlag(true);
- return result;
}
///
- public async Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid)
+ public void TakeOutNodeToContainer(string canvasGuid, string nodeGuid)
{
SetProjectLoadingFlag(false);
- var result = await currentFlowEnvironment.TakeOutNodeToContainerAsync(canvasGuid,nodeGuid); // 装饰器调用
+ currentFlowEnvironment.TakeOutNodeToContainer(canvasGuid,nodeGuid); // 装饰器调用
SetProjectLoadingFlag(true);
- return result;
}
///
@@ -393,27 +403,27 @@ namespace Serein.NodeFlow.Env
}
///
- public async Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
+ public void SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
- return await currentFlowEnvironment.SetConnectPriorityInvoke(fromNodeGuid, toNodeGuid, connectionType);
+ currentFlowEnvironment.SetConnectPriorityInvoke(fromNodeGuid, toNodeGuid, connectionType);
}
///
- public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
+ public void RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
{
- return await currentFlowEnvironment.RemoveConnectInvokeAsync(canvasGuid, fromNodeGuid, toNodeGuid, connectionType);
+ currentFlowEnvironment.RemoveInvokeConnect(canvasGuid, fromNodeGuid, toNodeGuid, connectionType);
}
///
- public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex)
+ public void RemoveArgSourceConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex)
{
- return await currentFlowEnvironment.RemoveConnectArgSourceAsync(canvasGuid, fromNodeGuid, toNodeGuid, argIndex);
+ currentFlowEnvironment.RemoveArgSourceConnect(canvasGuid, fromNodeGuid, toNodeGuid, argIndex);
}
///
- public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid)
+ public void RemoveNode(string canvasGuid, string nodeGuid)
{
- return await currentFlowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid);
+ currentFlowEnvironment.RemoveNode(canvasGuid, nodeGuid);
}
@@ -460,9 +470,9 @@ namespace Serein.NodeFlow.Env
#endregion
///
- public async Task SetStartNodeAsync(string canvasGuid, string nodeGuid)
+ public void SetStartNode(string canvasGuid, string nodeGuid)
{
- return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid);
+ currentFlowEnvironment.SetStartNode(canvasGuid, nodeGuid);
}
///
@@ -504,6 +514,11 @@ namespace Serein.NodeFlow.Env
///
public void SetUIContextOperation(UIContextOperation uiContextOperation)
{
+ if(uiContextOperation is null)
+ {
+ return;
+ }
+ IOC.Register(() => uiContextOperation).Build();
currentFlowEnvironment.SetUIContextOperation(uiContextOperation);
}
@@ -545,9 +560,9 @@ namespace Serein.NodeFlow.Env
}
///
- public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
+ public void ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
{
- return await currentFlowEnvironment.ChangeParameter(nodeGuid, isAdd, paramIndex);
+ currentFlowEnvironment.ChangeParameter(nodeGuid, isAdd, paramIndex);
}
#region 流程依赖类库的接口
diff --git a/NodeFlow/Env/FlowEnvironmentEvent.cs b/NodeFlow/Env/FlowEnvironmentEvent.cs
index ba4f8f2..276150a 100644
--- a/NodeFlow/Env/FlowEnvironmentEvent.cs
+++ b/NodeFlow/Env/FlowEnvironmentEvent.cs
@@ -26,92 +26,92 @@ namespace Serein.NodeFlow.Env
public void OnDllLoad(LoadDllEventArgs eventArgs)
{
- DllLoad.Invoke(eventArgs);
+ DllLoad?.Invoke(eventArgs);
}
public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
{
- ProjectLoaded.Invoke(eventArgs);
+ ProjectLoaded?.Invoke(eventArgs);
}
public void OnProjectSaving(ProjectSavingEventArgs eventArgs)
{
- ProjectSaving.Invoke(eventArgs);
+ ProjectSaving?.Invoke(eventArgs);
}
public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs)
{
- NodeConnectChanged.Invoke(eventArgs);
+ NodeConnectChanged?.Invoke(eventArgs);
}
public void OnCanvasCreated(CanvasCreateEventArgs eventArgs)
{
- CanvasCreated.Invoke(eventArgs);
+ CanvasCreated?.Invoke(eventArgs);
}
public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs)
{
- CanvasRemoved.Invoke(eventArgs);
+ CanvasRemoved?.Invoke(eventArgs);
}
public void OnNodeCreated(NodeCreateEventArgs eventArgs)
{
- NodeCreated.Invoke(eventArgs);
+ NodeCreated?.Invoke(eventArgs);
}
public void OnNodeRemoved(NodeRemoveEventArgs eventArgs)
{
- NodeRemoved.Invoke(eventArgs);
+ NodeRemoved?.Invoke(eventArgs);
}
public void OnNodePlace(NodePlaceEventArgs eventArgs)
{
- NodePlace.Invoke(eventArgs);
+ NodePlace?.Invoke(eventArgs);
}
public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
{
- NodeTakeOut.Invoke(eventArgs);
+ NodeTakeOut?.Invoke(eventArgs);
}
public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs)
{
- StartNodeChanged.Invoke(eventArgs);
+ StartNodeChanged?.Invoke(eventArgs);
}
public void OnFlowRunComplete(FlowEventArgs eventArgs)
{
- FlowRunComplete.Invoke(eventArgs);
+ FlowRunComplete?.Invoke(eventArgs);
}
public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs)
{
- MonitorObjectChanged.Invoke(eventArgs);
+ MonitorObjectChanged?.Invoke(eventArgs);
}
public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs)
{
- NodeInterruptStateChanged.Invoke(eventArgs);
+ NodeInterruptStateChanged?.Invoke(eventArgs);
}
public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs)
{
- InterruptTriggered.Invoke(eventArgs);
+ InterruptTriggered?.Invoke(eventArgs);
}
public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs)
{
- IOCMembersChanged.Invoke(eventArgs);
+ IOCMembersChanged?.Invoke(eventArgs);
}
public void OnNodeLocated(NodeLocatedEventArgs eventArgs)
{
- NodeLocated.Invoke(eventArgs);
+ NodeLocated?.Invoke(eventArgs);
}
public void OnEnvOutput(InfoType type, string value)
{
- EnvOutput.Invoke(type, value);
+ EnvOutput?.Invoke(type, value);
}
diff --git a/NodeFlow/Env/LocalFlowEnvironment.cs b/NodeFlow/Env/LocalFlowEnvironment.cs
index 135bc66..236d219 100644
--- a/NodeFlow/Env/LocalFlowEnvironment.cs
+++ b/NodeFlow/Env/LocalFlowEnvironment.cs
@@ -89,6 +89,9 @@ namespace Serein.NodeFlow.Env
#endregion
}
+
+
+
#region 远程管理
private MsgControllerOfServer clientMsgManage;
diff --git a/NodeFlow/Env/LocalFlowEnvironment_T.cs b/NodeFlow/Env/LocalFlowEnvironment_T.cs
new file mode 100644
index 0000000..0c8ff34
--- /dev/null
+++ b/NodeFlow/Env/LocalFlowEnvironment_T.cs
@@ -0,0 +1,2170 @@
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.Library.FlowNode;
+using Serein.Library.Utils;
+using Serein.Library.Utils.SereinExpression;
+using Serein.NodeFlow.Model;
+using Serein.NodeFlow.Model.Node;
+using Serein.NodeFlow.Services;
+using Serein.NodeFlow.Tool;
+using System;
+using System.Collections.Specialized;
+using System.Diagnostics;
+using System.Net.Http.Headers;
+using System.Net.Mime;
+using System.Reactive;
+using System.Reflection;
+using System.Text;
+
+namespace Serein.NodeFlow.Env
+{
+
+ ///
+ /// 运行环境
+ ///
+ public class LocalFlowEnvironment : IFlowEnvironment/*, IFlowEnvironmentEvent*/
+ {
+ ///
+ /// 节点的命名空间
+ ///
+ public const string SpaceName = $"{nameof(Serein)}.{nameof(NodeFlow)}.{nameof(Model)}";
+ public const string ThemeKey = "theme";
+ public const string DataKey = "data";
+ public const string MsgIdKey = "msgid";
+
+ ///
+ /// 流程运行环境
+ ///
+ public LocalFlowEnvironment(IFlowEnvironmentEvent flowEnvironmentEvent,
+ NodeMVVMService nodeMVVMService,
+ FlowLibraryManagement flowLibraryManagement,
+ FlowModelService flowModelService,
+ ISereinIOC sereinIOC)
+ {
+ this.FlowModelService = flowModelService;
+ this.Event = flowEnvironmentEvent;
+ this.FlowLibraryManagement = flowLibraryManagement; // 实例化类库管理
+ this.NodeMVVMManagement = nodeMVVMService;
+ this.FlowEnvironmentIOC = sereinIOC;
+ this.IsGlobalInterrupt = false;
+
+ #region 注册基本节点类型
+ NodeMVVMManagement.RegisterModel(NodeControlType.UI, typeof(SingleUINode)); // 动作节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.Action, typeof(SingleActionNode)); // 动作节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.Flipflop, typeof(SingleFlipflopNode)); // 触发器节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.ExpOp, typeof(SingleExpOpNode)); // 表达式节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.ExpCondition, typeof(SingleConditionNode)); // 条件表达式节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.GlobalData, typeof(SingleGlobalDataNode)); // 全局数据节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.Script, typeof(SingleScriptNode)); // 脚本节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.NetScript, typeof(SingleNetScriptNode)); // 脚本节点
+ NodeMVVMManagement.RegisterModel(NodeControlType.FlowCall, typeof(SingleFlowCallNode)); // 流程调用节点
+ #endregion
+
+ }
+
+ #region 远程管理
+
+ private MsgControllerOfServer clientMsgManage;
+
+ ///
+ /// 表示是否正在控制远程
+ /// Local control remote env
+ ///
+ public bool IsControlRemoteEnv { get; set; }
+
+ ///
+ /// 打开远程管理
+ ///
+ ///
+ public async Task StartRemoteServerAsync(int port = 7525)
+ {
+ if (clientMsgManage is null)
+ {
+ clientMsgManage = new MsgControllerOfServer(this);
+ //clientMsgManage = new MsgControllerOfServer(this,"123456");
+ }
+ _ = clientMsgManage.StartRemoteServerAsync(port);
+ }
+
+ ///
+ /// 结束远程管理
+ ///
+ public void StopRemoteServer()
+ {
+ try
+ {
+ clientMsgManage.StopRemoteServer();
+ }
+ catch (Exception ex)
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, "结束远程管理异常:" + ex);
+ }
+ }
+
+ #endregion
+
+ #region 环境运行事件
+ /*///
+ /// 加载Dll
+ ///
+ public event LoadDllHandler? DllLoad;
+
+ ///
+ /// 移除DLL
+ ///
+ public event RemoteDllHandler? OnDllRemote;
+
+ ///
+ /// 项目加载完成
+ ///
+ public event ProjectLoadedHandler? ProjectLoaded;
+
+ ///
+ /// 项目准备保存
+ ///
+ public event ProjectSavingHandler? ProjectSaving;
+
+ ///
+ /// 节点连接属性改变事件
+ ///
+ public event NodeConnectChangeHandler? NodeConnectChanged;
+
+ ///
+ /// 节点创建事件
+ ///
+ public event NodeCreateHandler? NodeCreated;
+
+ ///
+ /// 移除节点事件
+ ///
+ public event NodeRemoveHandler? NodeRemoved;
+
+ ///
+ /// 节点放置事件
+ ///
+ public event NodePlaceHandler NodePlace;
+
+ ///
+ /// 节点取出事件
+ ///
+ public event NodeTakeOutHandler NodeTakeOut;
+
+ ///
+ /// 起始节点变化事件
+ ///
+ public event StartNodeChangeHandler? StartNodeChanged;
+
+ ///
+ /// 流程运行完成事件
+ ///
+ public event FlowRunCompleteHandler? FlowRunComplete;
+
+ ///
+ /// 被监视的对象改变事件
+ ///
+ public event MonitorObjectChangeHandler? MonitorObjectChanged;
+
+ ///
+ /// 节点中断状态改变事件
+ ///
+ public event NodeInterruptStateChangeHandler? NodeInterruptStateChanged;
+
+ ///
+ /// 节点触发了中断
+ ///
+ public event ExpInterruptTriggerHandler? InterruptTriggered;
+
+ ///
+ /// 容器改变
+ ///
+ public event IOCMembersChangedHandler? IOCMembersChanged;
+
+ ///
+ /// 节点需要定位
+ ///
+ public event NodeLocatedHandler? NodeLocated;
+
+ ///
+ /// 运行环境输出
+ ///
+ public event EnvOutHandler? EnvOutput;
+
+ ///
+ /// 本地环境添加了画布
+ ///
+ public event CanvasCreateHandler CanvasCreated;
+
+ ///
+ /// 本地环境移除了画布
+ ///
+ public event CanvasRemoveHandler CanvasRemoved;
+*/
+ #endregion
+
+ #region 属性
+ ///
+ /// 运行环境的IOC容器
+ ///
+ public ISereinIOC FlowEnvironmentIOC { get; set; }
+
+ ///
+ /// 当前环境
+ ///
+ public IFlowEnvironment CurrentEnv { get => this; }
+
+ ///
+ /// 流程事件
+ ///
+ public IFlowEnvironmentEvent Event { get; set; }
+
+ ///
+ /// UI线程操作类
+ ///
+ public UIContextOperation UIContextOperation { get; private set; }
+
+ ///
+ /// 节点视图模型管理类
+ ///
+ public NodeMVVMService NodeMVVMManagement { get; set; }
+
+ ///
+ /// 节点管理服务()
+ ///
+ internal FlowModelService FlowModelService { get; set; }
+
+ ///
+ /// 信息输出等级
+ ///
+ public InfoClass InfoClass { get; set; } = InfoClass.Trivial;
+
+ ///
+ /// 如果没有全局触发器,且没有循环分支,流程执行完成后自动为 Completion 。
+ ///
+ public RunState FlowState { get; set; } = RunState.NoStart;
+ ///
+ /// 如果全局触发器还在运行,则为 Running 。
+ ///
+ public RunState FlipFlopState { get; set; } = RunState.NoStart;
+
+ ///
+ /// 环境名称
+ ///
+ public string EnvName { get; set; } = SpaceName;
+
+
+ ///
+ /// 本地加载的项目文件路径
+ ///
+ public string ProjectFileLocation { get; set; } = string.Empty;
+
+ ///
+ /// 是否全局中断
+ ///
+ public bool IsGlobalInterrupt { get; set; }
+
+ /////
+ ///// 流程中断器
+ /////
+ //public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; }
+
+ ///
+ /// 单例模式IOC容器,内部维护了一个实例字典,默认使用类型的FullName作为Key,如果以“接口-实现类”的方式注册,那么将使用接口类型的FullName作为Key。
+ /// 当某个类型注册绑定成功后,将不会因为其它地方尝试注册相同类型的行为导致类型被重新创建。
+ ///
+ public ISereinIOC IOC
+ {
+ get
+ {
+ if (FlowTaskIOC is null)
+ {
+ FlowTaskIOC = new SereinIOC();
+ }
+ return FlowTaskIOC;
+ }
+ }
+
+ #endregion
+
+ #region 私有变量
+
+ ///
+ /// IOC容器
+ ///
+ private ISereinIOC FlowTaskIOC { get; set; }
+
+ ///
+ /// 通过程序集名称管理动态加载的程序集,用于节点创建提供方法描述,流程运行时提供Emit委托
+ ///
+ private readonly FlowLibraryManagement FlowLibraryManagement;
+
+ ///
+ /// IOC对象容器管理
+ ///
+ private readonly SereinIOC sereinIOC;
+
+ ///
+ /// 本地运行环境缓存的持久化实例
+ ///
+ private Dictionary PersistennceInstance { get; } = new Dictionary();
+
+ ///
+ /// 环境加载的节点集合
+ /// Node Guid - Node Model
+ ///
+ private Dictionary NodeModels { get; } = [];
+
+ ///
+ /// 运行环境加载的画布集合
+ ///
+ private Dictionary FlowCanvass { get; } = [];
+
+ ///
+ /// 存放触发器节点(运行时全部调用)
+ ///
+ private List FlipflopNodes { get; } = [];
+
+
+ ///
+ /// 流程任务管理
+ ///
+ private FlowWorkManagement? flowTaskManagement;
+
+ #endregion
+
+ #region 环境对外接口
+
+ ///
+ /// 输出信息
+ ///
+ /// 日志内容
+ /// 日志类别
+ /// 日志级别
+ public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial)
+ {
+ if (@class >= this.InfoClass)
+ {
+ Event.OnEnvOutput(type, message);
+ }
+ //Console.WriteLine($"{DateTime.UtcNow} [{type}] : {message}{Environment.NewLine}");
+
+ }
+
+ ///
+ /// 异步运行
+ ///
+ ///
+ public async Task StartFlowAsync(string[] canvasGuids)
+ {
+ #region 校验参数
+ HashSet guids = new HashSet();
+ bool isBreak = false;
+ foreach (var canvasGuid in canvasGuids)
+ {
+ if (guids.Contains(canvasGuid))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"画布重复,停止运行。{canvasGuid}");
+ isBreak = true;
+ }
+ if (!FlowCanvass.ContainsKey(canvasGuid))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{canvasGuid}");
+ isBreak = true;
+ }
+ var count = NodeModels.Values.Count(n => n.CanvasDetails.Guid.Equals(canvasGuid));
+ if (count == 0)
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"画布没有节点,停止运行。{canvasGuid}");
+ isBreak = true;
+ }
+ else
+ {
+ guids.Add(canvasGuid);
+ }
+ }
+ if (isBreak)
+ {
+ guids.Clear();
+ return false;
+ }
+ #endregion
+
+
+ #region 初始化每个画布的数据,转换为流程任务
+ Dictionary flowTasks = [];
+ foreach (var guid in guids)
+ {
+ if (!TryGetCanvasModel(guid, out var canvasModel))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}");
+ return false;
+ }
+ var ft = new FlowTask();
+ ft.GetNodes = () => NodeModels.Values.Where(node => node.CanvasDetails.Guid.Equals(guid)).ToList();
+ if (canvasModel.StartNode?.Guid is null)
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}");
+ return false;
+ }
+ ft.GetStartNode = () => canvasModel.StartNode;
+ flowTasks.Add(guid, ft);
+ }
+ #endregion
+
+
+
+ IOC.Reset();
+ IOC.Register(); // 注册脚本接口
+
+ var flowTaskOptions = new FlowWorkOptions
+ {
+ Environment = this, // 流程
+ Flows = flowTasks,
+ FlowContextPool = new ObjectPool(() => new DynamicContext(this)), // 上下文对象池
+ AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), // 需要自动实例化的类型
+ InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
+ LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading),
+ ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit),
+ };
+
+
+
+ flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
+ var cts = new CancellationTokenSource();
+ try
+ {
+ var t = await flowTaskManagement.RunAsync(cts.Token);
+ }
+ catch (Exception ex)
+ {
+ SereinEnv.WriteLine(ex);
+ }
+ finally
+ {
+
+ SereinEnv.WriteLine(InfoType.INFO, $"流程运行完毕{Environment.NewLine}"); ;
+ }
+ flowTaskOptions = null;
+ return true;
+
+
+ }
+
+
+ ///
+ /// 从选定节点开始运行
+ ///
+ ///
+ ///
+ public async Task StartFlowFromSelectNodeAsync(string startNodeGuid)
+ {
+
+ if (!TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
+ {
+ return false;
+ }
+ var context = new DynamicContext(this);
+ var cts = new CancellationTokenSource();
+ await nodeModel.StartFlowAsync(context, cts.Token);
+ cts.Cancel();
+ cts.Dispose();
+ return true;
+
+ /* if (flowTaskManagement is null)
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, "没有启动流程,无法运行单个节点");
+ return false;
+ }
+ if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running)
+ {
+ if (!TryGetNodeModel(startNodeGuid, out var nodeModel) || nodeModel is SingleFlipflopNode)
+ {
+ return false;
+ }
+ var context = new DynamicContext(this);
+ var cts = new CancellationTokenSource();
+ await nodeModel.StartFlowAsync(context, cts.Token);
+ cts.Cancel();
+ cts.Dispose();
+ return true;
+ }
+ else
+ {
+ return false;
+ }*/
+ }
+
+ /*///
+ /// 单独运行一个节点
+ ///
+ ///
+ ///
+ ///
+ public async Task
private Dictionary NodeModels { get; } = [];
-
public ISereinIOC IOC => throw new NotImplementedException();
///
@@ -73,7 +72,7 @@ namespace Serein.NodeFlow.Env
public IFlowEnvironment CurrentEnv => this;
public UIContextOperation UIContextOperation { get; }
- public NodeMVVMManagement NodeMVVMManagement { get; }
+ public NodeMVVMService NodeMVVMManagement { get; }
///
@@ -658,8 +657,8 @@ namespace Serein.NodeFlow.Env
Event.OnNodeConnectChanged(new NodeConnectChangeEventArgs(canvasGuid,
fromNodeGuid,
toNodeGuid,
- JunctionOfConnectionType.Arg,
argIndex,
+ JunctionOfConnectionType.Arg,
argSourceType,
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
}
@@ -729,8 +728,8 @@ namespace Serein.NodeFlow.Env
Event.OnNodeConnectChanged(new NodeConnectChangeEventArgs(canvasGuid,
fromNodeGuid,
toNodeGuid,
- JunctionOfConnectionType.Arg,
argIndex,
+ JunctionOfConnectionType.Arg,
ConnectionArgSourceType.GetPreviousNodeData,
NodeConnectChangeEventArgs.ConnectChangeType.Remove)); // 通知UI
});
@@ -1283,8 +1282,8 @@ namespace Serein.NodeFlow.Env
canvasGuid,
fromNode.Guid, // 从哪个节点开始
toNode.Guid, // 连接到那个节点
+ pd.Index, // 连接线的样式类型
JunctionOfConnectionType.Arg,
- (int)pd.Index, // 连接线的样式类型
pd.ArgDataSourceType,
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
))); // 通知UI
diff --git a/NodeFlow/FlowNodeExtension.cs b/NodeFlow/FlowNodeExtension.cs
index 1b63c82..ebdca1f 100644
--- a/NodeFlow/FlowNodeExtension.cs
+++ b/NodeFlow/FlowNodeExtension.cs
@@ -1,7 +1,7 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
-using Serein.NodeFlow.Model;
+using Serein.NodeFlow.Model.Node;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Reflection;
diff --git a/NodeFlow/Model/NodeModelBaseData.cs b/NodeFlow/Model/Node/NodeModelBaseData.cs
similarity index 89%
rename from NodeFlow/Model/NodeModelBaseData.cs
rename to NodeFlow/Model/Node/NodeModelBaseData.cs
index 4fbbdfe..e023e77 100644
--- a/NodeFlow/Model/NodeModelBaseData.cs
+++ b/NodeFlow/Model/Node/NodeModelBaseData.cs
@@ -96,11 +96,19 @@ namespace Serein.NodeFlow.Model
{
PreviousNodes = new Dictionary>();
SuccessorNodes = new Dictionary>();
+ NeedResultNodes = new Dictionary>();
+
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
{
PreviousNodes[ctType] = new List();
SuccessorNodes[ctType] = new List();
}
+
+ foreach (ConnectionArgSourceType ctType in NodeStaticConfig.ConnectionArgSourceTypes)
+ {
+ NeedResultNodes[ctType] = new List();
+ }
+
ChildrenNode = new List();
DebugSetting = new NodeDebugSetting(this);
this.Env = environment;
@@ -116,6 +124,11 @@ namespace Serein.NodeFlow.Model
/// 不同分支的子节点(流程调用)
///
public Dictionary> SuccessorNodes { get; set; }
+
+ ///
+ /// 需要该节点返回值作为入参参数的节点集合
+ ///
+ public Dictionary> NeedResultNodes { get;}
///
/// 该节点的容器节点
diff --git a/NodeFlow/Model/NodeModelBaseFunc.cs b/NodeFlow/Model/Node/NodeModelBaseFunc.cs
similarity index 99%
rename from NodeFlow/Model/NodeModelBaseFunc.cs
rename to NodeFlow/Model/Node/NodeModelBaseFunc.cs
index a56f1db..d1ce2d8 100644
--- a/NodeFlow/Model/NodeModelBaseFunc.cs
+++ b/NodeFlow/Model/Node/NodeModelBaseFunc.cs
@@ -57,7 +57,7 @@ namespace Serein.NodeFlow.Model
return;
}
- ///
+ /* ///
/// 移除该节点
///
public virtual void Remove()
@@ -101,7 +101,7 @@ namespace Serein.NodeFlow.Model
this.DisplayName = null;
this.Env = null;
- }
+ }*/
///
/// 执行节点对应的方法
diff --git a/NodeFlow/Model/SingleActionNode.cs b/NodeFlow/Model/Node/SingleActionNode.cs
similarity index 90%
rename from NodeFlow/Model/SingleActionNode.cs
rename to NodeFlow/Model/Node/SingleActionNode.cs
index 496b1fb..8f807e9 100644
--- a/NodeFlow/Model/SingleActionNode.cs
+++ b/NodeFlow/Model/Node/SingleActionNode.cs
@@ -2,7 +2,7 @@
using Serein.Library;
using System.Security.AccessControl;
-namespace Serein.NodeFlow.Model
+namespace Serein.NodeFlow.Model.Node
{
///
/// 单动作节点(用于动作控件)
diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/Node/SingleConditionNode.cs
similarity index 100%
rename from NodeFlow/Model/SingleConditionNode.cs
rename to NodeFlow/Model/Node/SingleConditionNode.cs
diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/Node/SingleExpOpNode.cs
similarity index 100%
rename from NodeFlow/Model/SingleExpOpNode.cs
rename to NodeFlow/Model/Node/SingleExpOpNode.cs
diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/Node/SingleFlipflopNode.cs
similarity index 85%
rename from NodeFlow/Model/SingleFlipflopNode.cs
rename to NodeFlow/Model/Node/SingleFlipflopNode.cs
index f20f15c..ab479e6 100644
--- a/NodeFlow/Model/SingleFlipflopNode.cs
+++ b/NodeFlow/Model/Node/SingleFlipflopNode.cs
@@ -3,7 +3,7 @@ using Serein.Library;
using Serein.Library.Utils;
using System;
-namespace Serein.NodeFlow.Model
+namespace Serein.NodeFlow.Model.Node
{
///
/// 触发器节点
@@ -27,9 +27,9 @@ namespace Serein.NodeFlow.Model
#region 执行前中断
if (DebugSetting.IsInterrupt) // 执行触发前
{
- string guid = this.Guid.ToString();
- await this.DebugSetting.GetInterruptTask.Invoke();
- await Console.Out.WriteLineAsync($"[{this.MethodDetails.MethodName}]中断已取消,开始执行后继分支");
+ string guid = Guid.ToString();
+ await DebugSetting.GetInterruptTask.Invoke();
+ await Console.Out.WriteLineAsync($"[{MethodDetails.MethodName}]中断已取消,开始执行后继分支");
}
#endregion
@@ -61,7 +61,7 @@ namespace Serein.NodeFlow.Model
if (dynamicFlipflopContext.Type == TriggerDescription.Overtime)
{
- throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
+ throw new FlipflopException(MethodDetails.MethodName + "触发器超时触发。Guid" + Guid);
}
object result = dynamicFlipflopContext.Value;
var flowReslt = new FlowResult(this, context, result);
diff --git a/NodeFlow/Model/SingleFlowCallNode.cs b/NodeFlow/Model/Node/SingleFlowCallNode.cs
similarity index 98%
rename from NodeFlow/Model/SingleFlowCallNode.cs
rename to NodeFlow/Model/Node/SingleFlowCallNode.cs
index cf0a2b5..7987162 100644
--- a/NodeFlow/Model/SingleFlowCallNode.cs
+++ b/NodeFlow/Model/Node/SingleFlowCallNode.cs
@@ -119,7 +119,7 @@ namespace Serein.NodeFlow.Model
partial void OnIsShareParamChanged(bool value)
{
- if (targetNode is null)
+ if (targetNode is null || targetNode.MethodDetails is null)
{
return;
}
@@ -219,14 +219,13 @@ namespace Serein.NodeFlow.Model
}
- public override void Remove()
+ /*public override void Remove()
{
var tmp = this;
targetNode = null;
CacheMethodDetails = null;
-
}
-
+ */
///
diff --git a/NodeFlow/Model/SingleGlobalDataNode.cs b/NodeFlow/Model/Node/SingleGlobalDataNode.cs
similarity index 97%
rename from NodeFlow/Model/SingleGlobalDataNode.cs
rename to NodeFlow/Model/Node/SingleGlobalDataNode.cs
index b74fddb..6bda89a 100644
--- a/NodeFlow/Model/SingleGlobalDataNode.cs
+++ b/NodeFlow/Model/Node/SingleGlobalDataNode.cs
@@ -96,7 +96,7 @@ namespace Serein.NodeFlow.Model
{
foreach (var nodeModel in ChildrenNode)
{
- await nodeModel.Env.TakeOutNodeToContainerAsync(nodeModel.CanvasDetails.Guid, nodeModel.Guid);
+ nodeModel.Env.TakeOutNodeToContainer(nodeModel.CanvasDetails.Guid, nodeModel.Guid);
}
DataNode = null;
}
@@ -162,7 +162,7 @@ namespace Serein.NodeFlow.Model
KeyName = nodeInfo.CustomData?.KeyName;
}
- ///
+ /* ///
/// 需要移除数据节点
///
public override void Remove()
@@ -172,7 +172,7 @@ namespace Serein.NodeFlow.Model
}
// 移除数据节点
_ = this.Env.RemoveNodeAsync(DataNode.CanvasDetails.Guid, DataNode.Guid);
- }
+ }*/
}
}
diff --git a/NodeFlow/Model/SingleNetScriptNode.cs b/NodeFlow/Model/Node/SingleNetScriptNode.cs
similarity index 100%
rename from NodeFlow/Model/SingleNetScriptNode.cs
rename to NodeFlow/Model/Node/SingleNetScriptNode.cs
diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/Node/SingleScriptNode.cs
similarity index 99%
rename from NodeFlow/Model/SingleScriptNode.cs
rename to NodeFlow/Model/Node/SingleScriptNode.cs
index 9927089..0956ace 100644
--- a/NodeFlow/Model/SingleScriptNode.cs
+++ b/NodeFlow/Model/Node/SingleScriptNode.cs
@@ -226,7 +226,7 @@ namespace Serein.NodeFlow.Model
scriptContext.OnExit();
};
- var envEvent = (IFlowEnvironmentEvent)context.Env;
+ var envEvent = context.Env.Event;
envEvent.FlowRunComplete += onFlowStop; // 防止运行后台流程
if (token.IsCancellationRequested) return null;
diff --git a/NodeFlow/Model/SingleUINode.cs b/NodeFlow/Model/Node/SingleUINode.cs
similarity index 94%
rename from NodeFlow/Model/SingleUINode.cs
rename to NodeFlow/Model/Node/SingleUINode.cs
index 42111b8..7ce9c9a 100644
--- a/NodeFlow/Model/SingleUINode.cs
+++ b/NodeFlow/Model/Node/SingleUINode.cs
@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Serein.NodeFlow.Model
+namespace Serein.NodeFlow.Model.Node
{
public class SingleUINode : NodeModelBase
{
@@ -24,7 +24,7 @@ namespace Serein.NodeFlow.Model
var result = await base.ExecutingAsync(context, token);
if (result.Value is IEmbeddedContent adapter)
{
- this.Adapter = adapter;
+ Adapter = adapter;
context.NextOrientation = ConnectionInvokeType.IsSucceed;
}
else
diff --git a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
new file mode 100644
index 0000000..e3fc394
--- /dev/null
+++ b/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs
@@ -0,0 +1,451 @@
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.NodeFlow.Env;
+using Serein.NodeFlow.Model.Node;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static Serein.Library.Api.NodeConnectChangeEventArgs;
+
+namespace Serein.NodeFlow.Model.Operation
+{
+ ///
+ /// 节点连接状态发生改变
+ ///
+ internal class ChangeNodeConnectionOperation : OperationBase
+ {
+ public override string Theme => nameof(ChangeNodeConnectionOperation);
+
+ ///
+ /// 所在画布
+ ///
+ public required string CanvasGuid { get; set; }
+
+ ///
+ /// 连接关系中始节点的Guid
+ ///
+ public required string FromNodeGuid { get; set; }
+
+ ///
+ /// 连接关系中目标节点的Guid
+ ///
+ public required string ToNodeGuid { get; set; }
+
+ ///
+ /// 起始节点连接控制点类型
+ ///
+ public JunctionType FromNodeJunctionType { get; set; }
+
+ ///
+ /// 目标节点连接控制点类型
+ ///
+ public JunctionType ToNodeJunctionType { get; set; }
+
+ /// 连接类型
+ ///
+ public ConnectionInvokeType ConnectionInvokeType { get; set; }
+ ///
+ /// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
+ ///
+ public ConnectChangeType ChangeType { get; set; }
+ ///
+ /// 指示需要创建什么类型的连接线
+ ///
+ public JunctionOfConnectionType JunctionOfConnectionType { get; set; } = JunctionOfConnectionType.None;
+ ///
+ /// 节点对应的方法入参所需参数来源
+ ///
+ public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
+
+ ///
+ /// 第几个参数
+ ///
+ 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 bool Execute()
+ {
+ 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) => CreateInvokeConnection(), // 创建节点之间的调用关系
+ (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => RemoveInvokeConnection(), // 移除节点之间的调用关系
+ (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Create) => CreateArgConnection(), // 创建节点之间的参数传递关系
+ (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => RemoveArgConnection(), // 移除节点之间的参数传递关系
+ _ => false
+ };
+ return state;
+ }
+
+ public override void ToInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+ ///
+ /// 创建方法调用关系
+ ///
+ private bool CreateInvokeConnection()
+ {
+ 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 检查是否存在对应的连接
+ 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); // 添加到目标节点新类别的父分支
+ flowEnvironmentEvent.OnNodeConnectChanged(
+ new NodeConnectChangeEventArgs(
+ FlowCanvas.Guid,
+ fromNode.Guid, // 从哪个节点开始
+ toNode.Guid, // 连接到那个节点
+ JunctionOfConnectionType.Invoke,
+ invokeType, // 连接线的样式类型
+ NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
+ ));
+ // Invoke
+ // GetResult
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+
+ }
+
+ ///
+ /// 移除方法调用关系
+ ///
+ private bool RemoveInvokeConnection()
+ {
+ FromNode.SuccessorNodes[ConnectionInvokeType].Remove(ToNode);
+ ToNode.PreviousNodes[ConnectionInvokeType].Remove(FromNode);
+
+ 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;
+ }
+
+ ///
+ /// 创建参数连接关系
+ ///
+ ///
+ private bool CreateArgConnection()
+ {
+ IFlowNode fromNodeControl = ToNode;
+ IFlowNode toNodeControl = ToNode;
+ ConnectionArgSourceType type = ConnectionArgSourceType;
+ int index = ArgIndex;
+
+
+
+ var toNodeArgSourceGuid = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid; // 目标节点对应参数可能已经有其它连接
+ var toNodeArgSourceType = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
+
+ if (FromNode.Guid == toNodeArgSourceGuid
+ && toNodeArgSourceType == ConnectionArgSourceType)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系,此次操作将不会执行" +
+ $"起始节点:{FromNode.Guid}" +
+ $"目标节点:{ToNode.Guid}" +
+ $"参数索引:{ArgIndex}" +
+ $"参数类型:{ConnectionArgSourceType}");
+ /*flowEnvironmentEvent.OnNodeConnectChanged(
+ new NodeConnectChangeEventArgs(
+ FlowCanvas.Guid,
+ FromNode.Guid, // 从哪个节点开始
+ ToNode.Guid, // 连接到那个节点
+ ArgIndex, // 连接线的样式类型
+ JunctionOfConnectionType.Arg,
+ ConnectionArgSourceType,
+ NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
+ )); // 通知UI */
+ return true;
+ }
+
+ if (!string.IsNullOrEmpty(toNodeArgSourceGuid)) // 更改关系获取
+ {
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null;
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
+ flowEnvironmentEvent.OnNodeConnectChanged(
+ new NodeConnectChangeEventArgs(
+ FlowCanvas.Guid,
+ FromNode.Guid,
+ ToNode.Guid,
+ ArgIndex,
+ JunctionOfConnectionType.Arg,
+ ConnectionArgSourceType.GetPreviousNodeData,
+ NodeConnectChangeEventArgs.ConnectChangeType.Remove));
+ }
+
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = FromNode.Guid; // 设置
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType;
+
+ flowEnvironmentEvent.OnNodeConnectChanged(
+ new NodeConnectChangeEventArgs(
+ FlowCanvas.Guid,
+ FromNode.Guid, // 从哪个节点开始
+ ToNode.Guid, // 连接到那个节点
+ ArgIndex, // 连接线的样式类型
+ JunctionOfConnectionType.Arg,
+ ConnectionArgSourceType,
+ NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
+ )); // 通知UI
+ return true;
+
+ }
+
+ ///
+ /// 移除参数连接关系
+ ///
+ ///
+ ///
+ ///
+ private bool RemoveArgConnection()
+ {
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceNodeGuid = null;
+ ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值
+
+ if (OperatingSystem.IsWindows())
+ {
+ flowEnvironmentEvent.OnNodeConnectChanged(
+ new NodeConnectChangeEventArgs(
+ FlowCanvas.Guid,
+ FromNode.Guid,
+ ToNode.Guid,
+ ArgIndex,
+ JunctionOfConnectionType.Arg,
+ ConnectionArgSourceType.GetPreviousNodeData,
+ NodeConnectChangeEventArgs.ConnectChangeType.Remove));
+ }
+ return true;
+ }
+
+ ///
+ /// 检查连接是否合法
+ ///
+ /// 发起连接的起始节点
+ /// 要连接的目标节点
+ /// 发起连接节点的控制点类型
+ /// 被连接节点的控制点类型
+ ///
+ 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);
+ }
+
+ }
+}
diff --git a/NodeFlow/Model/Operation/ChangeParameterOperation.cs b/NodeFlow/Model/Operation/ChangeParameterOperation.cs
new file mode 100644
index 0000000..6269fc1
--- /dev/null
+++ b/NodeFlow/Model/Operation/ChangeParameterOperation.cs
@@ -0,0 +1,90 @@
+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.Operation
+{
+ 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 bool Execute()
+ {
+ if (!ValidationParameter()) return false;
+
+ if (IsAdd)
+ {
+ if (nodeModel.MethodDetails.AddParamsArg(ParamIndex))
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+ else
+ {
+ if (nodeModel.MethodDetails.RemoveParamsArg(ParamIndex))
+ {
+ return true;
+ }
+ else
+ {
+ return true;
+ }
+ }
+ }
+
+ public override void ToInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+
+ }
+}
diff --git a/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs b/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs
new file mode 100644
index 0000000..828d5b8
--- /dev/null
+++ b/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs
@@ -0,0 +1,98 @@
+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.Operation
+{
+ ///
+ /// 放置节点操作
+ ///
+ internal class ContainerPlaceNodeOperation : OperationBase
+ {
+ public override string Theme => nameof(ContainerPlaceNodeOperation);
+
+ ///
+ /// 所在画布
+ ///
+ public string CanvasGuid { get; set; }
+
+ ///
+ /// 子节点,该数据为此次事件的主节点
+ ///
+ public string NodeGuid { get; set; }
+ ///
+ /// 父节点
+ ///
+ public string ContainerNodeGuid { get; set; }
+
+
+ ///
+ /// 父节点
+ ///
+ private INodeContainer ContainerNode;
+
+ ///
+ /// 子节点,该数据为此次事件的主节点
+ ///
+ 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 bool Execute()
+ {
+ if (!ValidationParameter()) return false;
+
+ ContainerNode.PlaceNode(Node);
+ flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, ContainerNodeGuid)); // 通知UI更改节点放置位置
+ return true;
+ }
+
+ public override bool Undo()
+ {
+ ContainerNode.TakeOutNode(Node);
+ flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, NodeGuid)); // 重新放置在画布上
+ return true;
+ }
+
+
+ public override void ToInfo()
+ {
+ }
+
+
+ }
+}
diff --git a/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs b/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs
new file mode 100644
index 0000000..650dd91
--- /dev/null
+++ b/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs
@@ -0,0 +1,89 @@
+using Serein.Library.Api;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.NodeFlow.Model.Operation
+{
+
+ ///
+ /// 取出节点操作
+ ///
+ internal class ContainerTakeOutNodeOperation : OperationBase
+ {
+ public override string Theme => nameof(ContainerTakeOutNodeOperation);
+
+ ///
+ /// 所在画布
+ ///
+ public string CanvasGuid { get; set; }
+
+ ///
+ /// 子节点,该数据为此次事件的主节点
+ ///
+ public string NodeGuid { get; set; }
+
+
+ ///
+ /// 父节点
+ ///
+ private INodeContainer ContainerNode;
+
+ ///
+ /// 子节点,该数据为此次事件的主节点
+ ///
+ private IFlowNode Node;
+
+
+
+ public override bool ValidationParameter()
+ {
+ if (!flowModelService.ContainsCanvasModel(CanvasGuid))
+ {
+ flowEnvironment.WriteLine(Library.InfoType.INFO, $"节点取出失败,目标画布不存在[{NodeGuid}]");
+ return false;
+ }
+ // 获取目标节点与容器节点
+ if (!flowModelService.TryGetNodeModel(NodeGuid, out var nodeModel))
+ {
+ flowEnvironment.WriteLine(Library.InfoType.INFO, $"节点取出失败,目标节点不存在[{NodeGuid}]");
+ return false;
+ }
+ if (nodeModel.ContainerNode is not INodeContainer containerNode)
+ {
+ flowEnvironment.WriteLine(Library.InfoType.INFO, $"节点取出失败,节点并非容器节点[{nodeModel.Guid}]");
+ return false;
+ }
+ Node = nodeModel;
+ ContainerNode = containerNode;
+ return true;
+ }
+
+ public override bool Execute()
+ {
+ if (!ValidationParameter()) return false;
+
+ ContainerNode.TakeOutNode(Node);
+ flowEnvironmentEvent.OnNodeTakeOut(new NodeTakeOutEventArgs(CanvasGuid, NodeGuid)); // 重新放置在画布上
+ return true;
+ }
+
+ public override bool Undo()
+ {
+ ContainerNode.PlaceNode(Node);
+ if (ContainerNode is IFlowNode containerFlowNode)
+ {
+ flowEnvironmentEvent.OnNodePlace(new NodePlaceEventArgs(CanvasGuid, NodeGuid, containerFlowNode.Guid)); // 通知UI更改节点放置位置
+
+ }
+ return true;
+ }
+
+
+ public override void ToInfo()
+ {
+ }
+ }
+}
diff --git a/NodeFlow/Model/Operation/CreateCanvasOperation.cs b/NodeFlow/Model/Operation/CreateCanvasOperation.cs
new file mode 100644
index 0000000..747f6b1
--- /dev/null
+++ b/NodeFlow/Model/Operation/CreateCanvasOperation.cs
@@ -0,0 +1,51 @@
+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.Operation
+{
+ 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 bool Execute()
+ {
+ if(!ValidationParameter()) return false;
+
+ var cavasnModel = new FlowCanvasDetails(flowEnvironment);
+ cavasnModel.LoadInfo(CanvasInfo);
+ flowModelService.AddCanvasModel(cavasnModel);
+ this.flowCanvasDetails = cavasnModel; ;
+ flowEnvironmentEvent.OnCanvasCreated(new CanvasCreateEventArgs(cavasnModel));
+ return true;
+ }
+
+ public override void ToInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+ }
+}
diff --git a/NodeFlow/Model/Operation/CreateNodeOperation.cs b/NodeFlow/Model/Operation/CreateNodeOperation.cs
new file mode 100644
index 0000000..e28fa66
--- /dev/null
+++ b/NodeFlow/Model/Operation/CreateNodeOperation.cs
@@ -0,0 +1,145 @@
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.NodeFlow.Model.Node;
+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.Operation
+{
+ 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; }
+
+
+ ///
+ /// 是否为基础节点
+ ///
+ private bool IsBaseNode => NodeControlType.IsBaseNode();
+
+ ///
+ /// 执行成功后所创建的节点
+ ///
+ private IFlowNode? flowNode;
+
+ ///
+ /// 节点所在画布
+ ///
+ 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 bool Execute()
+ {
+ if (!ValidationParameter()) return false; // 执行时验证
+
+ IFlowNode? nodeModel;
+ if (IsBaseNode)
+ {
+ nodeModel = FlowNodeExtension.CreateNode(flowEnvironment, 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, NodeControlType, methodDetails); // 一般的加载节点方法
+ }
+
+ nodeModel.Guid ??= Guid.NewGuid().ToString();
+ nodeModel.Position = Position; // 设置位置
+
+ // 节点与画布互相绑定
+ nodeModel.CanvasDetails = flowCanvasDetails;
+ flowCanvasDetails.Nodes.Add(nodeModel);
+
+ flowModelService.AddNodeModel(nodeModel);
+ this.flowNode = nodeModel;
+ 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.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;
+ }*/
+ }
+}
diff --git a/NodeFlow/Model/Operation/OperationBase.cs b/NodeFlow/Model/Operation/OperationBase.cs
index 92ae550..86ca91d 100644
--- a/NodeFlow/Model/Operation/OperationBase.cs
+++ b/NodeFlow/Model/Operation/OperationBase.cs
@@ -1,92 +1,118 @@
-using System;
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.NodeFlow.Services;
+using Serein.NodeFlow.Tool;
+using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
{
+ internal interface IOperation
+ {
+ ///
+ /// 用于判断是否可以撤销
+ ///
+ bool IsCanUndo { get; }
+ ///
+ /// 执行操作前验证数据
+ ///
+ ///
+ bool ValidationParameter();
+ ///
+ /// 执行操作
+ ///
+ bool Execute();
+ ///
+ /// 撤销操作
+ ///
+ bool Undo();
+ }
- class Test {
+ internal abstract class OperationBase : IOperation
+ {
+ ///
+ /// 运行环境
+ ///
+ [AutoInjection]
+ protected IFlowEnvironment flowEnvironment;
///
- /// 撤销栈
+ /// 节点管理服务
///
- private Stack undoStack = [];
+ [AutoInjection]
+ protected FlowModelService flowModelService;
+
///
- /// 重做栈
+ /// 流程依赖服务
///
- private Stack redoStack = [];
+ [AutoInjection]
+ protected FlowLibraryManagement flowLibraryManagement;
+
+ ///
+ /// 流程事件服务
+ ///
+ [AutoInjection]
+ protected IFlowEnvironmentEvent flowEnvironmentEvent;
+
+ public abstract string Theme { get;}
+
+ ///
+ /// 是否支持特效
+ ///
+ public virtual bool IsCanUndo => true;
- /*
- // 执行新命令时,将命令推入撤销栈,并清空重做栈
- undoStack.Push(operation);
- redoStack.Clear();
- */
+ ///
+ /// 验证参数
+ ///
+ ///
+ public abstract bool ValidationParameter();
+ ///
+ /// 执行
+ ///
+ public abstract bool Execute();
///
/// 撤销
///
- public void Undo()
+ public virtual bool Undo()
{
- if (undoStack.Count > 0)
+ if (!IsCanUndo)
{
- var command = undoStack.Pop();
- command.Undo(); // 执行撤销
- redoStack.Push(command); // 将撤销的命令推入重做栈
+ Debug.WriteLine($"该操作暂未提供撤销功能[{Theme}]");
+ return false;
}
+ return true;
}
///
- /// 重做
+ /// 导出操作信息
///
- public void Redo()
- {
- if (redoStack.Count > 0)
- {
- var command = redoStack.Pop();
- command.Execute();
- undoStack.Push(command); // 将重做的命令推入撤销栈
- }
- }
+ public abstract void ToInfo();
+
+
}
-
internal class OperationInfo
{
}
- internal abstract class OperationBase : IOperation
- {
- ///
- /// 操作的主题
- ///
- public required string Theme { get; set; }
- public abstract void Execute();
- public abstract void Undo();
- public abstract void ToInfo();
+ class Test {
- protected OperationBase()
- {
-
- }
- protected OperationBase(OperationInfo info)
- {
-
- }
+
}
- internal interface IOperation
- {
- void Execute(); // 执行操作
- void Undo(); // 撤销操作
-
- }
+
+
+
}
diff --git a/NodeFlow/Model/Operation/RemoveCanvasOperation.cs b/NodeFlow/Model/Operation/RemoveCanvasOperation.cs
new file mode 100644
index 0000000..fc25d4a
--- /dev/null
+++ b/NodeFlow/Model/Operation/RemoveCanvasOperation.cs
@@ -0,0 +1,59 @@
+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.Operation
+{
+ 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 bool Execute()
+ {
+ 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();
+ flowEnvironmentEvent.OnCanvasRemoved(new CanvasRemoveEventArgs(flowCanvasDetails.Guid));
+ return true;
+ }
+
+ public override void ToInfo()
+ {
+ throw new NotImplementedException();
+ }
+
+ }
+}
diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operation/RemoveNodeOperation.cs
new file mode 100644
index 0000000..d99d014
--- /dev/null
+++ b/NodeFlow/Model/Operation/RemoveNodeOperation.cs
@@ -0,0 +1,216 @@
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.Script.Node;
+using System;
+using System.Collections.Generic;
+using System.Data.Common;
+using System.Linq;
+using System.Reflection.Metadata;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.NodeFlow.Model.Operation
+{
+ 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; }
+
+ ///
+ /// 节点所在画布
+ ///
+ private FlowCanvasDetails flowCanvasDetails;
+ ///
+ /// 被删除的节点
+ ///
+ private IFlowNode flowNode;
+
+ ///
+ /// 移除节点时删除连线所触发的事件参数的缓存
+ ///
+ private List EventArgs { get; } = new List();
+
+ 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 bool Execute()
+ {
+ if (!ValidationParameter()) return false;
+
+ // 需要移除对应的方法调用、以及参数获取调用
+ // 还需要记录移除的事件参数,用以撤销恢复
+
+ #region 移除方法调用关系
+ foreach (var item in flowNode.PreviousNodes)
+ {
+
+ var connectionType = item.Key; // 连接类型
+ var previousNodes = item.Value; // 对应类型的父节点集合
+ foreach (IFlowNode previousNode in previousNodes)
+ {
+ 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); // 缓存事件参数
+ flowEnvironmentEvent.OnNodeConnectChanged(e);
+
+ }
+ }
+
+ if (flowNode.ControlType == NodeControlType.FlowCall)
+ {
+ // 根据流程接口节点目前的设计,暂未支持能连接下一个节点
+ }
+ else
+ {
+ // 遍历所有后继节点,从那些后继节点中的前置节点集合中移除该节点
+ foreach (var item in flowNode.SuccessorNodes)
+ {
+
+ var connectionType = item.Key; // 方法调用连接类型
+ var successorNodes = item.Value; // 对应类型的父节点集合
+ foreach (IFlowNode successorNode in successorNodes)
+ {
+ successorNode.SuccessorNodes[connectionType].Remove(flowNode);
+ var e = new NodeConnectChangeEventArgs(
+ CanvasGuid, // 画布
+ flowNode.Guid, // 被移除的节点Guid
+ successorNode.Guid, // 子节点Guid
+ JunctionOfConnectionType.Invoke, // 方法调用关系
+ connectionType, // 对应的连接关系
+ NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
+ EventArgs.Add(e); // 缓存事件参数
+ flowEnvironmentEvent.OnNodeConnectChanged(e);
+ }
+ }
+ }
+
+ #endregion
+
+ #region 移除参数获取关系
+ // 需要找到有哪些节点的入参参数,被设置为了该节点,然后将其删除
+ // 因为节点自身没有记录哪些节点选取了自己作为参数来源节点,所以需要遍历所有节点
+
+ foreach (var item in flowNode.NeedResultNodes)
+ {
+ var connectionType = item.Key; // 参数来源连接类型
+ var argNodes = item.Value; // 对应类型的入参需求节点集合
+ 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;
+ // 找到了对应的入参控制点了
+ var e = new NodeConnectChangeEventArgs(
+ CanvasGuid, // 画布
+ flowNode.Guid, // 被移除的节点Guid
+ argNode.Guid, // 子节点Guid
+ parameter.Index, // 作用在第几个参数上,用于指示移除第几个参数的连线
+ JunctionOfConnectionType.Arg, // 指示移除的是参数连接线
+ connectionType, // 对应的连接关系
+ NodeConnectChangeEventArgs.ConnectChangeType.Remove); // 移除连线
+ EventArgs.Add(e); // 缓存事件参数
+ flowEnvironmentEvent.OnNodeConnectChanged(e);
+ }
+ }
+ }
+ #endregion
+
+ flowModelService.RemoveNodeModel(flowNode); // 从记录中移除
+ //flowNode.Remove(); // 调用节点的移除方法
+
+ if(flowEnvironment.UIContextOperation is null)
+ {
+ flowCanvasDetails?.Nodes.Remove(flowNode);
+ }
+ else
+ {
+ // 存在UI上下文操作,当前运行环境极有可能运行在有UI线程的平台上
+ // 为了避免直接修改 ObservableCollection 集合导致异常产生,故而使用UI线程上下文操作运行
+ flowEnvironment.UIContextOperation?.Invoke(() =>
+ {
+ flowCanvasDetails?.Nodes.Remove(flowNode);
+ });
+ }
+ flowEnvironmentEvent.OnNodeRemoved(new NodeRemoveEventArgs(CanvasGuid, NodeGuid));
+ 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();
+ }
+ }
+}
diff --git a/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs b/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs
new file mode 100644
index 0000000..8b0e4ce
--- /dev/null
+++ b/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs
@@ -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.Operation
+{
+
+ ///
+ /// 将调用顺序置为优先
+ ///
+ 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;
+
+ }
+
+ ///
+ /// 成为首项
+ ///
+ public override bool Execute()
+ {
+ if(!ValidationParameter()) return 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 true;
+ }
+
+ ///
+ /// 恢复原来的位置
+ ///
+ 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();
+ }
+
+
+ }
+}
diff --git a/NodeFlow/Model/Operation/SetStartNodeOperation.cs b/NodeFlow/Model/Operation/SetStartNodeOperation.cs
new file mode 100644
index 0000000..184c1e1
--- /dev/null
+++ b/NodeFlow/Model/Operation/SetStartNodeOperation.cs
@@ -0,0 +1,78 @@
+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.Operation
+{
+ ///
+ /// 设置起始节点
+ ///
+ 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 bool Execute()
+ {
+ if (!ValidationParameter()) return false;
+
+ if (CanvasModel.StartNode is not null
+ && flowModelService.TryGetNodeModel(CanvasModel.StartNode.Guid, out var flowNode))
+ {
+ OldStartNodeModel = flowNode;
+ }
+
+ CanvasModel.StartNode = NewStartNodeModel;
+ 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();
+ }
+
+
+ }
+}
diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj
index 17b8984..60d8a0f 100644
--- a/NodeFlow/Serein.NodeFlow.csproj
+++ b/NodeFlow/Serein.NodeFlow.csproj
@@ -40,6 +40,12 @@
+
+
+
+
+
+
diff --git a/NodeFlow/Services/FlowModelService.cs b/NodeFlow/Services/FlowModelService.cs
new file mode 100644
index 0000000..a759281
--- /dev/null
+++ b/NodeFlow/Services/FlowModelService.cs
@@ -0,0 +1,99 @@
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.NodeFlow.Model.Node;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.NodeFlow.Services
+{
+ public class FlowModelService
+ {
+ private readonly IFlowEnvironment environment;
+
+ public FlowModelService(IFlowEnvironment environment)
+ {
+ this.environment = environment;
+ }
+
+ ///
+ /// 环境加载的节点集合
+ /// Node Guid - Node Model
+ ///
+ private Dictionary NodeModels { get; } = [];
+
+ ///
+ /// 运行环境加载的画布集合
+ ///
+ private Dictionary FlowCanvass { get; } = [];
+
+ ///
+ /// 存放触发器节点(运行时全部调用)
+ ///
+ private List FlipflopNodes { get; } = [];
+
+ public IFlowNode? GetNodeModel(string guid)
+ {
+ NodeModels.TryGetValue(guid, out var nodeModel);
+ return nodeModel;
+ }
+
+ public FlowCanvasDetails? GetCanvasModel(string guid)
+ {
+ FlowCanvass.TryGetValue(guid, out var nodeModel);
+ return nodeModel;
+ }
+
+ public bool TryGetNodeModel(string guid,out IFlowNode flowNode)
+ {
+ return NodeModels.TryGetValue(guid, out flowNode!);
+ }
+
+ public bool TryGetCanvasModel(string guid,out FlowCanvasDetails flowCanvas)
+ {
+ return FlowCanvass.TryGetValue(guid, out flowCanvas!);;
+ }
+
+
+ public bool ContainsNodeModel(string guid)
+ {
+ return NodeModels.ContainsKey(guid);
+ }
+
+ public bool ContainsCanvasModel(string guid)
+ {
+ return FlowCanvass.ContainsKey(guid);
+ }
+
+ public bool AddNodeModel(IFlowNode flowNode)
+ {
+ ArgumentNullException.ThrowIfNull(flowNode);
+ ArgumentNullException.ThrowIfNull(flowNode.Guid);
+ return NodeModels.TryAdd(flowNode.Guid, flowNode);
+ }
+ public bool AddCanvasModel(FlowCanvasDetails flowCanvasDetails)
+ {
+ ArgumentNullException.ThrowIfNull(flowCanvasDetails);
+ ArgumentNullException.ThrowIfNull(flowCanvasDetails.Guid);
+ return FlowCanvass.TryAdd(flowCanvasDetails.Guid, flowCanvasDetails);
+ }
+ public bool RemoveNodeModel(IFlowNode flowNode)
+ {
+ ArgumentNullException.ThrowIfNull(flowNode.Guid);
+ return NodeModels.Remove(flowNode.Guid);
+ }
+ public bool RemoveCanvasModel(FlowCanvasDetails flowCanvasDetails)
+ {
+ ArgumentNullException.ThrowIfNull(flowCanvasDetails.Guid);
+ return FlowCanvass.Remove(flowCanvasDetails.Guid);
+ }
+
+ public List GetAllNodeModel() => [.. NodeModels.Values];
+ public List GetAllCanvasModel() => [.. FlowCanvass.Values];
+
+
+
+ }
+}
diff --git a/NodeFlow/Services/FlowOperationService.cs b/NodeFlow/Services/FlowOperationService.cs
new file mode 100644
index 0000000..ae9fa09
--- /dev/null
+++ b/NodeFlow/Services/FlowOperationService.cs
@@ -0,0 +1,81 @@
+using Serein.Library.Api;
+using Serein.NodeFlow.Model.Operation;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.NodeFlow.Services
+{
+ public class FlowOperationService
+ {
+ private readonly ISereinIOC sereinIOC;
+
+ public FlowOperationService(ISereinIOC sereinIOC)
+ {
+ this.sereinIOC = sereinIOC;
+ }
+
+ ///
+ /// 撤销栈
+ ///
+ private Stack undoStack = [];
+ ///
+ /// 重做栈
+ ///
+ private Stack redoStack = [];
+
+
+ /*
+ // 执行新命令时,将命令推入撤销栈,并清空重做栈
+ */
+ ///
+ /// 撤销
+ ///
+ public void Undo()
+ {
+ if (undoStack.Count > 0)
+ {
+ var command = undoStack.Pop();
+ var state = command.Undo(); // 执行撤销
+ if (state)
+ {
+ redoStack.Push(command); // 将撤销的命令推入重做栈
+
+ }
+ }
+ }
+
+ ///
+ /// 重做
+ ///
+ public void Redo()
+ {
+ if (redoStack.Count > 0)
+ {
+ var command = redoStack.Pop();
+ var state = command.Execute();
+ if (state)
+ {
+ undoStack.Push(command); // 将重做的命令推入撤销栈
+ }
+ }
+ }
+
+
+ internal void Execute(IOperation operation)
+ {
+ sereinIOC.InjectDependenciesProperty(operation); // 注入所需要的依赖
+ var state = operation.Execute();
+ if (state)
+ {
+ // 执行后,推入撤销栈,并清空重做栈
+ undoStack.Push(operation);
+ redoStack.Clear();
+ }
+
+ }
+
+ }
+}
diff --git a/NodeFlow/FlowWorkManagement.cs b/NodeFlow/Services/FlowWorkManagement.cs
similarity index 98%
rename from NodeFlow/FlowWorkManagement.cs
rename to NodeFlow/Services/FlowWorkManagement.cs
index c2ca0b2..7af53f0 100644
--- a/NodeFlow/FlowWorkManagement.cs
+++ b/NodeFlow/Services/FlowWorkManagement.cs
@@ -2,14 +2,14 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
-using Serein.NodeFlow.Model;
+using Serein.NodeFlow.Model.Node;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks.Dataflow;
using System.Xml.Linq;
-namespace Serein.NodeFlow
+namespace Serein.NodeFlow.Services
{
///
/// 流程任务管理
@@ -201,8 +201,8 @@ namespace Serein.NodeFlow
var pool = WorkOptions.FlowContextPool;
var ioc = WorkOptions.Environment.IOC;
- var fit = ioc.Get();
- fit.CancelAllTrigger(); // 取消所有中断
+ // var fit = ioc.Get();
+ // fit.CancelAllTrigger(); // 取消所有中断
foreach (var md in mds) // 结束时
{
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
diff --git a/NodeFlow/Services/NodeService.cs b/NodeFlow/Services/NodeService.cs
deleted file mode 100644
index a1f6391..0000000
--- a/NodeFlow/Services/NodeService.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
-namespace Serein.NodeFlow.Services
-{
- internal class NodeService
- {
- }
-}
diff --git a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs
index 30df405..0630b65 100644
--- a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs
+++ b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs
@@ -40,7 +40,7 @@ namespace Serein.Workbench.Customs
///
/// FlowMethodInfoListBox.xaml 的交互逻辑
///
- public partial class FlowMethodInfoListBox : UserControl,System.ComponentModel.INotifyPropertyChanged
+ public partial class FlowMethodInfoListBox : UserControl, System.ComponentModel.INotifyPropertyChanged
{
private object viewMethodInfo;
public object ViewMethodInfo
diff --git a/Workbench/Node/Junction/JunctionControlBase.cs b/Workbench/Node/Junction/JunctionControlBase.cs
index dcb9f1d..eb1fe6e 100644
--- a/Workbench/Node/Junction/JunctionControlBase.cs
+++ b/Workbench/Node/Junction/JunctionControlBase.cs
@@ -50,7 +50,7 @@ namespace Serein.Workbench.Node.View
this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除
this.MouseMove += ParamsArgControl_MouseMove;
this.MouseLeave += ParamsArgControl_MouseLeave;
- AddOrRemoveParamsTask = AddParamAsync;
+ AddOrRemoveParamsAction = AddParamAsync;
}
@@ -112,11 +112,11 @@ namespace Serein.Workbench.Node.View
private bool isMouseOver; // 鼠标悬停状态
- private Func AddOrRemoveParamsTask; // 增加或删除参数
+ private Action AddOrRemoveParamsAction; // 增加或删除参数
- public async void ParamsArg_OnMouseDown(object sender, MouseButtonEventArgs e)
+ public void ParamsArg_OnMouseDown(object sender, MouseButtonEventArgs e)
{
- await AddOrRemoveParamsTask.Invoke();
+ AddOrRemoveParamsAction.Invoke();
}
private void ParamsArgControl_MouseMove(object sender, MouseEventArgs e)
@@ -133,7 +133,7 @@ namespace Serein.Workbench.Node.View
// 如果焦点仍在控件上时,则改变点击事件
if (isMouseOver)
{
- AddOrRemoveParamsTask = RemoveParamAsync;
+ AddOrRemoveParamsAction = RemoveParamAsync;
this.Dispatcher.Invoke(InvalidateVisual);// 触发一次重绘
}
@@ -149,7 +149,7 @@ namespace Serein.Workbench.Node.View
private void ParamsArgControl_MouseLeave(object sender, MouseEventArgs e)
{
isMouseOver = false;
- AddOrRemoveParamsTask = AddParamAsync; // 鼠标焦点离开时恢复点击事件
+ AddOrRemoveParamsAction = AddParamAsync; // 鼠标焦点离开时恢复点击事件
cts?.Cancel();
this.Dispatcher.Invoke(InvalidateVisual);// 触发一次重绘
@@ -157,13 +157,13 @@ namespace Serein.Workbench.Node.View
- private async Task AddParamAsync()
+ private void AddParamAsync()
{
- await this.MyNode.Env.ChangeParameter(MyNode.Guid, true, ArgIndex);
+ this.MyNode.Env.ChangeParameter(MyNode.Guid, true, ArgIndex);
}
- private async Task RemoveParamAsync()
+ private void RemoveParamAsync()
{
- await this.MyNode.Env.ChangeParameter(MyNode.Guid, false, ArgIndex);
+ this.MyNode.Env.ChangeParameter(MyNode.Guid, false, ArgIndex);
}
}
diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs
index 6d27972..0cbbb30 100644
--- a/Workbench/Node/View/ConnectionControl.cs
+++ b/Workbench/Node/View/ConnectionControl.cs
@@ -243,11 +243,11 @@ namespace Serein.Workbench.Node.View
var jctEnd = End.JunctionType.ToConnectyionType();
if (jct == JunctionOfConnectionType.Invoke)
{
- env.RemoveConnectInvokeAsync(canvasGuid, Start.MyNode.Guid, End.MyNode.Guid, InvokeType);
+ env.RemoveInvokeConnect(canvasGuid, Start.MyNode.Guid, End.MyNode.Guid, InvokeType);
}
else if (jct == JunctionOfConnectionType.Arg)
{
- env.RemoveConnectArgSourceAsync(canvasGuid,Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ;
+ env.RemoveArgSourceConnect(canvasGuid,Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ;
}
}
diff --git a/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs b/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs
index 8325690..38139e4 100644
--- a/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs
+++ b/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs
@@ -1,4 +1,4 @@
-using Serein.NodeFlow.Model;
+using Serein.NodeFlow.Model.Node;
using Serein.Workbench.Node.View;
namespace Serein.Workbench.Node.ViewModel
diff --git a/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs b/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs
index 3ab4e2e..57caa6a 100644
--- a/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs
+++ b/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs
@@ -1,4 +1,4 @@
-using Serein.NodeFlow.Model;
+using Serein.NodeFlow.Model.Node;
using Serein.Workbench.Node.View;
namespace Serein.Workbench.Node.ViewModel
diff --git a/Workbench/Node/ViewModel/UINodeControlViewModel.cs b/Workbench/Node/ViewModel/UINodeControlViewModel.cs
index 01fabea..94904a5 100644
--- a/Workbench/Node/ViewModel/UINodeControlViewModel.cs
+++ b/Workbench/Node/ViewModel/UINodeControlViewModel.cs
@@ -1,7 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
using Serein.Library.Api;
-using Serein.NodeFlow.Model;
+using Serein.NodeFlow.Model.Node;
using System;
using System.Collections.Generic;
using System.Linq;
diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs
index 131fa85..1a0e93c 100644
--- a/Workbench/Services/FlowEEForwardingService.cs
+++ b/Workbench/Services/FlowEEForwardingService.cs
@@ -27,7 +27,7 @@ namespace Serein.Workbench.Services
///
private readonly IFlowEnvironment flowEnvironment;
private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
- private readonly UIContextOperation uIContextOperation;
+ private readonly UIContextOperation uiContextOperation;
///
/// 转发流程运行环境各个事件的实现类
@@ -41,7 +41,7 @@ namespace Serein.Workbench.Services
{
this.flowEnvironment = flowEnvironment;
this.flowEnvironmentEvent = flowEnvironmentEvent;
- this.uIContextOperation = uIContextOperation;
+ this.uiContextOperation = uIContextOperation;
InitFlowEnvironmentEvent();
}
@@ -184,7 +184,7 @@ namespace Serein.Workbench.Services
///
private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value)
{
- uIContextOperation.Invoke(() =>
+ uiContextOperation.Invoke(() =>
{
EnvOutput?.Invoke(type, value);
});
@@ -245,7 +245,10 @@ namespace Serein.Workbench.Services
///
private void FlowEnvironmentEvent_OnCanvasCreate(CanvasCreateEventArgs eventArgs)
{
- CanvasCreated?.Invoke(eventArgs);
+ uiContextOperation?.Invoke(() =>
+ {
+ CanvasCreated?.Invoke(eventArgs);
+ });
}
///
diff --git a/Workbench/Services/FlowNodeService.cs b/Workbench/Services/FlowNodeService.cs
index ce75c20..509794f 100644
--- a/Workbench/Services/FlowNodeService.cs
+++ b/Workbench/Services/FlowNodeService.cs
@@ -656,11 +656,7 @@ namespace Serein.Workbench.Services
{
int width = 1200;
int height = 780;
- _ = Task.Run(async () =>
- {
- var result = await flowEnvironment.CreateCanvasAsync("", width, height);
- Console.WriteLine(result.Guid);
- });
+ flowEnvironment.CreateCanvas("", width, height);
}
///
@@ -673,7 +669,7 @@ namespace Serein.Workbench.Services
return;
}
var model = ((FlowCanvasViewModel)CurrentSelectCanvas.DataContext).Model;
- _ = flowEnvironment.RemoveCanvasAsync(model.Guid);
+ flowEnvironment.RemoveCanvas(model.Guid);
}
///
@@ -692,7 +688,7 @@ namespace Serein.Workbench.Services
{
return;
}
- _ = flowEnvironment.CreateNodeAsync(canvasGuid, nodeType, position, methodDetailsInfo);
+ flowEnvironment.CreateNode(canvasGuid, nodeType, position, methodDetailsInfo);
}
///
@@ -711,7 +707,7 @@ namespace Serein.Workbench.Services
return;
}
- _ = flowEnvironment.RemoveNodeAsync(model.CanvasDetails.Guid, model.Guid);
+ flowEnvironment.RemoveNode(model.CanvasDetails.Guid, model.Guid);
}
#endregion
diff --git a/Workbench/Services/FlowProjectService.cs b/Workbench/Services/FlowProjectService.cs
index 7bc033e..bee1c80 100644
--- a/Workbench/Services/FlowProjectService.cs
+++ b/Workbench/Services/FlowProjectService.cs
@@ -23,6 +23,11 @@ namespace Serein.Workbench.Services
this.flowEnvironment = flowEnvironment;
}
+ public void StartProjectManagementServer()
+ {
+ // CollabrationSideManagement
+ }
+
public void LoadLocalProject(string filePath)
{
if (File.Exists(filePath))
diff --git a/Workbench/Themes/MethodDetailsControl.xaml.cs b/Workbench/Themes/MethodDetailsControl.xaml.cs
index 1be57d8..a25cd1f 100644
--- a/Workbench/Themes/MethodDetailsControl.xaml.cs
+++ b/Workbench/Themes/MethodDetailsControl.xaml.cs
@@ -123,7 +123,7 @@ namespace Serein.Workbench.Themes
private void ExecuteAddParams(object parameter)
{
// 方法逻辑
- this.MethodDetails.AddParamsArg();
+ this.MethodDetails.AddParamsArg(0);
}
diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs
index 722ac4d..c8f5e8e 100644
--- a/Workbench/ViewModels/MainMenuBarViewModel.cs
+++ b/Workbench/ViewModels/MainMenuBarViewModel.cs
@@ -8,7 +8,7 @@ namespace Serein.Workbench.ViewModels
{
public class MainMenuBarViewModel : ObservableObject
{
- private readonly IFlowEnvironment environment;
+ private readonly IFlowEnvironment flowEnvironment;
private readonly FlowNodeService flowNodeService;
private readonly FlowProjectService flowProjectService;
@@ -61,12 +61,18 @@ namespace Serein.Workbench.ViewModels
public ICommand OpenDynamicCompilerCommand { get; private set; }
+ ///
+ /// 开启远程服务
+ ///
+ public ICommand OpenRemoteServerCommand { get; private set; }
- public MainMenuBarViewModel(IFlowEnvironment environment,
+
+
+ public MainMenuBarViewModel(IFlowEnvironment flowEnvironment,
FlowNodeService flowNodeService,
FlowProjectService flowProjectService)
{
- this.environment = environment;
+ this.flowEnvironment = flowEnvironment;
this.flowNodeService = flowNodeService;
this.flowProjectService = flowProjectService;
SaveProjectCommand = new RelayCommand(SaveProject); // 保存项目
@@ -81,29 +87,51 @@ namespace Serein.Workbench.ViewModels
StopCurrentCanvasFlowCommand = new RelayCommand(StopCurrentCanvasFlow); // 停止当前流程
OpenEnvOutWindowCommand = new RelayCommand(OpenEnvOutWindow); // 打开运行输出窗口
- OpenDynamicCompilerCommand = new RelayCommand(OpenDynamicCompiler); // 打开动态编译仓库窗口
+ OpenDynamicCompilerCommand = new RelayCommand(OpenDynamicCompiler); // 打开动态编译窗口
+
+ OpenRemoteServerCommand = new RelayCommand(OpenRemoteServer); // 打开动态编译窗口
this.flowProjectService = flowProjectService;
}
- private void SaveProject() => environment.SaveProject(); // 保存项目
- private void LoadLocalProject() {
+ private void SaveProject() => flowEnvironment.SaveProject(); // 保存项目
+ private void LoadLocalProject()
+ {
flowProjectService.SelectProjectFile(); //选择项目
}
- private void LoadRemoteProject()
- {
+ private void LoadRemoteProject()
+ {
}
private void CreateFlowCanvas() => flowNodeService.CreateFlowCanvas();
private void RemoteFlowCanvas() => flowNodeService.RemoveFlowCanvas();
- private void StartUIFlow() => environment.StartFlowAsync([.. flowNodeService.FlowCanvass.Select(c => c.Guid)]);
- private void StartCurrentCanvasFlow() => environment.StartFlowAsync([flowNodeService.CurrentSelectCanvas.Guid]);
+ private void StartUIFlow()
+ {
+ var canvass = flowNodeService.FlowCanvass;
+ if(canvass.Length > 0)
+ {
+ string[] guids = [..canvass.Select(c => c.Guid)];
+ flowEnvironment.StartFlowAsync(guids);
+ }
+
+ }
+ private void StartCurrentCanvasFlow()
+ {
+ var canvas = flowNodeService.CurrentSelectCanvas;
+ if (canvas is null) return;
+ flowEnvironment.StartFlowAsync([canvas.Guid]);
+ }
private void StopCurrentCanvasFlow() { }
private void OpenDynamicCompiler() { }
private void OpenEnvOutWindow() => LogWindow.Instance?.Show();
+ private void OpenRemoteServer()
+ {
+ flowEnvironment.StartRemoteServerAsync();
+ }
+
}
}
diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs
index 2dc0fdf..a7569d4 100644
--- a/Workbench/Views/FlowCanvasView.xaml.cs
+++ b/Workbench/Views/FlowCanvasView.xaml.cs
@@ -372,7 +372,7 @@ namespace Serein.Workbench.Views
if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器
{
// 通知运行环境调用加载节点子项的方法
- _ = flowEnvironment.PlaceNodeToContainerAsync(Guid,
+ flowEnvironment.PlaceNodeToContainer(Guid,
nodeControl.ViewModel.NodeModel.Guid, // 待移动的节点
regionControl.ViewModel.NodeModel.Guid); // 目标的容器节点
return;
@@ -566,7 +566,8 @@ namespace Serein.Workbench.Views
// F5 调试当前选定节点
var nodeModel = selectNodeControls[0].ViewModel.NodeModel;
SereinEnv.WriteLine(InfoType.INFO, $"调试运行当前节点:{nodeModel.Guid}");
- _ = nodeModel.StartFlowAsync(new DynamicContext(flowEnvironment), new CancellationToken());
+ _ = flowEnvironment.StartFlowFromSelectNodeAsync(nodeModel.Guid);
+ //_ = nodeModel.StartFlowAsync(new DynamicContext(flowEnvironment), new CancellationToken());
}
}
@@ -898,7 +899,7 @@ namespace Serein.Workbench.Views
{
var canvasGuid = this.Guid;
- await flowEnvironment.ConnectInvokeNodeAsync(
+ flowEnvironment.ConnectInvokeNode(
canvasGuid,
cd.StartJunction.MyNode.Guid,
cd.CurrentJunction.MyNode.Guid,
@@ -922,7 +923,7 @@ namespace Serein.Workbench.Views
}
var canvasGuid = this.Guid;
- await flowEnvironment.ConnectArgSourceNodeAsync(
+ flowEnvironment.ConnectArgSourceNode(
canvasGuid,
cd.StartJunction.MyNode.Guid,
cd.CurrentJunction.MyNode.Guid,
@@ -1269,7 +1270,7 @@ namespace Serein.Workbench.Views
if (!string.IsNullOrEmpty(guid))
{
var canvasGuid = this.Guid;
- flowEnvironment.RemoveNodeAsync(canvasGuid, guid);
+ flowEnvironment.RemoveNode(canvasGuid, guid);
}
}
}
@@ -1491,10 +1492,10 @@ namespace Serein.Workbench.Views
- contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("设为起点", (s, e) => flowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid)));
+ contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("设为起点", (s, e) => flowEnvironment.SetStartNode(canvasGuid, nodeGuid)));
contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", async (s, e) =>
{
- var result = await flowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid);
+ flowEnvironment.RemoveNode(canvasGuid, nodeGuid);
}));
#region 右键菜单功能 - 控件对齐
diff --git a/Workbench/Views/MainMenuBarView.xaml b/Workbench/Views/MainMenuBarView.xaml
index c941476..56395c6 100644
--- a/Workbench/Views/MainMenuBarView.xaml
+++ b/Workbench/Views/MainMenuBarView.xaml
@@ -14,7 +14,7 @@
@@ -24,19 +24,19 @@
-
+
-
+
-
+
-
- -->
+