1. 重新设计了Generate项目及相关特性的命名,避免与其他类型混淆。

2. 补充了部分注释。
3. 修改了删除容器节点时,容器内子节点未正确删除的问题。
This commit is contained in:
fengjiayi
2025-07-30 21:15:07 +08:00
parent 93148b11a5
commit 152077e9b5
188 changed files with 2713 additions and 1406 deletions

View File

@@ -14,7 +14,7 @@ namespace Serein.FlowStartTool
{
public readonly IFlowEnvironment flowEnvironment = new FlowEnvironment();
public bool IsRuning;
public async Task StartFlow(SereinProjectData flowProjectData, string fileDataPath)
public void StartFlow(SereinProjectData flowProjectData, string fileDataPath)
{
IsRuning = true;
SynchronizationContext? uiContext = SynchronizationContext.Current; // 在UI线程上获取UI线程上下文信息

View File

@@ -7,6 +7,11 @@
/// <typeparam name="TValue"></typeparam>
public interface IEnumConvertor<TEnum, TValue>
{
/// <summary>
/// 将枚举值转换为指定类型的值
/// </summary>
/// <param name="e"></param>
/// <returns></returns>
TValue Convertor(TEnum e);
}

View File

@@ -252,11 +252,20 @@ namespace Serein.Library.Api
/// </summary>
public string Result { get; private set; }
/// <summary>
/// 上传当前节点的执行状态和结果信息。
/// </summary>
/// <param name="runState"></param>
public void UploadState(RunState runState)
{
State = runState;
TS = DateTime.Now - StateTime;
}
/// <summary>
/// 上传当前节点的执行结果值。
/// </summary>
/// <param name="value"></param>
public void UploadResultValue(object value = null)
{
if(value is null)
@@ -269,6 +278,11 @@ namespace Serein.Library.Api
Result = $"{type.FullName}::{value}";
}
}
/// <summary>
/// 上传当前节点的执行参数信息。
/// </summary>
/// <param name="values"></param>
public void UploadParameters(object[] values = null)
{
if (values is null)
@@ -282,6 +296,10 @@ namespace Serein.Library.Api
}
}
/// <summary>
/// 返回当前节点的执行信息字符串,包含状态、耗时和结果。
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"[{State}]{TS.TotalSeconds:0.000}ms : {Result}";

View File

@@ -4,6 +4,7 @@ using System.Threading.Tasks;
namespace Serein.Library.Api
{
#nullable enable
/// <summary>
/// 流程运行接口
/// </summary>
@@ -24,7 +25,7 @@ namespace Serein.Library.Api
/// </summary>
/// <param name="ioc"></param>
/// <param name="setDefultMemberOnReset">用于每次启动时重置IOC后默认注册某些类型</param>
void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC> setDefultMemberOnReset = null);
void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC>? setDefultMemberOnReset = null);
/// <summary>
/// 开始运行流程

View File

@@ -153,6 +153,9 @@ namespace Serein.Library.Api
/// </summary>
public class ProjectLoadedEventArgs : FlowEventArgs
{
/// <summary>
/// 项目加载完成事件参数
/// </summary>
public ProjectLoadedEventArgs()
{
}
@@ -163,6 +166,10 @@ namespace Serein.Library.Api
/// </summary>
public class ProjectSavingEventArgs : FlowEventArgs
{
/// <summary>
/// 项目保存事件参数
/// </summary>
/// <param name="projectData"></param>
public ProjectSavingEventArgs(SereinProjectData projectData)
{
ProjectData = projectData;
@@ -179,6 +186,10 @@ namespace Serein.Library.Api
/// </summary>
public class LoadDllEventArgs : FlowEventArgs
{
/// <summary>
/// 加载了DLL外部依赖事件参数
/// </summary>
/// <param name="nodeLibraryInfo"></param>
public LoadDllEventArgs(FlowLibraryInfo nodeLibraryInfo)
{
this.NodeLibraryInfo = nodeLibraryInfo;
@@ -194,6 +205,9 @@ namespace Serein.Library.Api
/// </summary>
public class RemoteDllEventArgs : FlowEventArgs
{
/// <summary>
/// 移除了DLL外部依赖事件参数
/// </summary>
public RemoteDllEventArgs()
{
}
@@ -270,6 +284,9 @@ namespace Serein.Library.Api
}
/// <summary>
/// 连接关系所在的画布Guid
/// </summary>
public string CanvasGuid { get; }
/// <summary>
@@ -309,11 +326,18 @@ namespace Serein.Library.Api
/// </summary>
public class CanvasCreateEventArgs : FlowEventArgs
{
/// <summary>
/// 画布添加事件参数
/// </summary>
/// <param name="model"></param>
public CanvasCreateEventArgs(FlowCanvasDetails model)
{
Model = model;
}
/// <summary>
/// 画布
/// </summary>
public FlowCanvasDetails Model { get; }
}
@@ -322,11 +346,18 @@ namespace Serein.Library.Api
/// </summary>
public class CanvasRemoveEventArgs : FlowEventArgs
{
/// <summary>
/// 画布移除事件参数
/// </summary>
/// <param name="canvasGuid"></param>
public CanvasRemoveEventArgs(string canvasGuid)
{
CanvasGuid = canvasGuid;
}
/// <summary>
/// 所处画布Guid
/// </summary>
public string CanvasGuid { get; }
}
@@ -360,10 +391,6 @@ namespace Serein.Library.Api
/// 在UI上的位置
/// </summary>
public PositionOfUI Position { get; private set; }
/// <summary>
/// 容器
/// </summary>
//public string RegeionGuid { get; private set; }
}
/// <summary>
@@ -371,12 +398,20 @@ namespace Serein.Library.Api
/// </summary>
public class NodeRemoveEventArgs : FlowEventArgs
{
/// <summary>
/// 被移除节点事件参数
/// </summary>
/// <param name="canvasGuid"></param>
/// <param name="nodeGuid"></param>
public NodeRemoveEventArgs(string canvasGuid, string nodeGuid)
{
CanvasGuid = canvasGuid;
this.NodeGuid = nodeGuid;
}
/// <summary>
/// 被移除节点所在的画布Guid
/// </summary>
public string CanvasGuid { get; }
/// <summary>
@@ -390,6 +425,12 @@ namespace Serein.Library.Api
/// </summary>
public class NodePlaceEventArgs : FlowEventArgs
{
/// <summary>
/// 节点放置事件参数
/// </summary>
/// <param name="canvasGuid"></param>
/// <param name="nodeGuid"></param>
/// <param name="containerNodeGuid"></param>
public NodePlaceEventArgs(string canvasGuid, string nodeGuid, string containerNodeGuid)
{
CanvasGuid = canvasGuid;
@@ -397,6 +438,9 @@ namespace Serein.Library.Api
ContainerNodeGuid = containerNodeGuid;
}
/// <summary>
/// 画布Guid
/// </summary>
public string CanvasGuid { get; }
/// <summary>
@@ -414,6 +458,12 @@ namespace Serein.Library.Api
/// </summary>
public class NodeTakeOutEventArgs : FlowEventArgs
{
/// <summary>
/// 节点取出事件参数
/// </summary>
/// <param name="canvasGuid"></param>
/// <param name="containerNodeGuid"></param>
/// <param name="nodeGuid"></param>
public NodeTakeOutEventArgs(string canvasGuid, string containerNodeGuid, string nodeGuid)
{
CanvasGuid = canvasGuid;
@@ -421,6 +471,9 @@ namespace Serein.Library.Api
ContainerNodeGuid = containerNodeGuid;
}
/// <summary>
/// 所在画布Guid
/// </summary>
public string CanvasGuid { get; }
/// <summary>
@@ -438,9 +491,17 @@ namespace Serein.Library.Api
/// <summary>
/// 起始节点发生了变化
/// </summary>
public class StartNodeChangeEventArgs : FlowEventArgs
{
/// <summary>
/// 起始节点发生了变化事件参数
/// </summary>
/// <param name="canvasGuid"></param>
/// <param name="oldNodeGuid"></param>
/// <param name="newNodeGuid"></param>
public StartNodeChangeEventArgs(string canvasGuid, string oldNodeGuid, string newNodeGuid)
{
CanvasGuid = canvasGuid;
@@ -448,6 +509,9 @@ namespace Serein.Library.Api
this.NewNodeGuid = newNodeGuid; ;
}
/// <summary>
/// 所在画布Guid
/// </summary>
public string CanvasGuid { get; }
/// <summary>
@@ -515,6 +579,11 @@ namespace Serein.Library.Api
/// </summary>
public class NodeInterruptStateChangeEventArgs : FlowEventArgs
{
/// <summary>
/// 节点中断状态改变事件参数
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="isInterrupt"></param>
public NodeInterruptStateChangeEventArgs(string nodeGuid,bool isInterrupt)
{
NodeGuid = nodeGuid;
@@ -526,14 +595,19 @@ namespace Serein.Library.Api
/// 中断的节点Guid
/// </summary>
public string NodeGuid { get;}
/// <summary>
/// 是否中断
/// </summary>
public bool IsInterrupt { get;}
// public InterruptClass Class { get;}
}
/// <summary>
/// 节点触发了中断事件参数
/// </summary>
public class InterruptTriggerEventArgs : FlowEventArgs
{
/// <summary>
/// 中断触发类型
/// </summary>
public enum InterruptTriggerType
{
/// <summary>
@@ -550,6 +624,12 @@ namespace Serein.Library.Api
Obj,
}
/// <summary>
/// 中断触发事件参数
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="expression"></param>
/// <param name="type"></param>
public InterruptTriggerEventArgs(string nodeGuid, string expression, InterruptTriggerType type)
{
this.NodeGuid = nodeGuid;
@@ -561,7 +641,13 @@ namespace Serein.Library.Api
/// 中断的节点Guid
/// </summary>
public string NodeGuid { get;}
/// <summary>
/// 被触发的表达式
/// </summary>
public string Expression { get;}
/// <summary>
/// 中断触发类型
/// </summary>
public InterruptTriggerType Type { get;}
}
@@ -572,6 +658,9 @@ namespace Serein.Library.Api
/// </summary>
public class IOCMembersChangedEventArgs : FlowEventArgs
{
/// <summary>
/// IOC成员发生改变的事件类型
/// </summary>
public enum EventType
{
/// <summary>
@@ -583,12 +672,23 @@ namespace Serein.Library.Api
/// </summary>
Completeuild,
}
/// <summary>
/// IOC成员发生改变事件参数
/// </summary>
/// <param name="key"></param>
/// <param name="instance"></param>
public IOCMembersChangedEventArgs(string key, object instance)
{
this.Key = key;
this.Instance = instance;
}
/// <summary>
/// IOC成员发生改变事件参数
/// </summary>
public string Key { get; private set; }
/// <summary>
/// IOC成员发生改变事件参数
/// </summary>
public object Instance { get; private set; }
}
@@ -597,38 +697,20 @@ namespace Serein.Library.Api
/// </summary>
public class NodeLocatedEventArgs : FlowEventArgs
{
/// <summary>
/// 节点需要定位事件参数
/// </summary>
/// <param name="nodeGuid"></param>
public NodeLocatedEventArgs(string nodeGuid)
{
NodeGuid = nodeGuid;
}
/// <summary>
/// 节点需要定位事件参数
/// </summary>
public string NodeGuid { get; private set; }
}
/* /// <summary>
/// 节点移动了
/// </summary>
public class NodeMovedEventArgs : FlowEventArgs
{
public NodeMovedEventArgs(string nodeGuid, double x, double y)
{
this.NodeGuid = nodeGuid;
this.X = x;
this.Y = y;
}
/// <summary>
/// 节点唯一标识
/// </summary>
public string NodeGuid { get; private set; }
/// <summary>
/// 画布上的x坐标
/// </summary>
public double X { get; private set; }
/// <summary>
/// 画布上的y坐标
/// </summary>
public double Y { get; private set; }
}*/
#endregion
@@ -729,23 +811,113 @@ namespace Serein.Library.Api
/// </summary>
event EnvOutHandler EnvOutput;
/// <summary>
/// 加载了DLL外部依赖事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnDllLoad(LoadDllEventArgs eventArgs);
/// <summary>
/// 项目加载完成事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs);
/// <summary>
/// 项目准备保存事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnProjectSaving(ProjectSavingEventArgs eventArgs);
/// <summary>
/// 节点连接关系发生改变事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs);
/// <summary>
/// 画布创建事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnCanvasCreated(CanvasCreateEventArgs eventArgs);
/// <summary>
/// 画布移除事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs);
/// <summary>
/// 节点创建事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodeCreated(NodeCreateEventArgs eventArgs);
/// <summary>
/// 节点移除事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodeRemoved(NodeRemoveEventArgs eventArgs);
/// <summary>
/// 节点放置事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodePlace(NodePlaceEventArgs eventArgs);
/// <summary>
/// 节点取出事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs);
/// <summary>
/// 起始节点发生了变化事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs);
/// <summary>
/// 流程运行完成事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnFlowRunComplete(FlowEventArgs eventArgs);
/// <summary>
/// 被监视的对象发生了改变事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs);
/// <summary>
/// 节点中断状态发生了改变事件(开启了中断/取消了中断)
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs);
/// <summary>
/// 触发了中断事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs);
/// <summary>
/// IOC容器成员发生了改变事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs);
/// <summary>
/// 节点需要定位事件
/// </summary>
/// <param name="eventArgs"></param>
public void OnNodeLocated(NodeLocatedEventArgs eventArgs);
/// <summary>
/// 环境输出信息事件
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
public void OnEnvOutput(InfoType type, string value);
}
@@ -864,7 +1036,7 @@ namespace Serein.Library.Api
/// 获取当前项目信息
/// </summary>
/// <returns></returns>
Task<SereinProjectData> GetProjectInfoAsync();
SereinProjectData GetProjectInfoAsync();
#endregion

View File

@@ -1,12 +1,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks;
using Serein.Library;
namespace Serein.Library.Api
{
@@ -72,11 +67,11 @@ namespace Serein.Library.Api
MethodDetails MethodDetails { get; set; }
/// <summary>
/// 节点集合
/// 前继节点集合
/// </summary>
Dictionary<ConnectionInvokeType, List<IFlowNode>> PreviousNodes { get;}
/// <summary>
/// 节点集合
/// 后继节点集合
/// </summary>
Dictionary<ConnectionInvokeType, List<IFlowNode>> SuccessorNodes { get; set; }

View File

@@ -118,14 +118,14 @@ namespace Serein.Library.Api
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
IJsonToken CreateObject(IDictionary<string, object> values = null);
IJsonToken CreateObject(IDictionary<string, object>? values = null);
/// <summary>
/// 创建数组
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
IJsonToken CreateArray(IEnumerable<object> values = null);
IJsonToken CreateArray(IEnumerable<object>? values = null);
/// <summary>
/// 将对象转换为JSON Token自动转换为 JObject/JArray。

View File

@@ -11,7 +11,9 @@ namespace Serein.Library.Api
/// </summary>
public interface INodeContainer
{
/// <summary>
/// 容器节点的Guid与 IFlowNode.Guid 相同
/// </summary>
string Guid { get; }
/// <summary>
/// 放置一个节点

View File

@@ -20,20 +20,7 @@ namespace Serein.Library.Api
/// </summary>
IFlowNode NodeModel { get; }
/// <summary>
/// 根据索引从入参数据获取数据
/// </summary>
/// <param name="context"></param>
/// <param name="index"></param>
/// <returns></returns>
//object GetArgData(IDynamicContext context, int index);
/// <summary>
/// 获取流程当前传递的数据
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
/// FlowResult GetFlowData(IDynamicContext context);
/// <summary>
/// 获取全局数据
/// </summary>

View File

@@ -108,8 +108,32 @@ namespace Serein.Library.Api
/// <param name="action"></param>
/// <returns></returns>
ISereinIOC Run<T>(Action<T> action);
/// <summary>
/// 从容器中获取数个类型的实例进行运行
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
ISereinIOC Run<T1, T2>(Action<T1, T2> action);
/// <summary>
/// 从容器中获取数个类型的实例进行运行
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
ISereinIOC Run<T1, T2, T3>(Action<T1, T2, T3> action);
/// <summary>
/// 从容器中获取数个类型的实例进行运行
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="T4"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
ISereinIOC Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action);
}

View File

@@ -16,10 +16,17 @@ namespace Serein.Library
[AttributeUsage(AttributeTargets.Class)]
public sealed class AutoRegisterAttribute : Attribute
{
/// <summary>
/// 自动注册特性
/// </summary>
/// <param name="Class"></param>
public AutoRegisterAttribute(RegisterSequence Class = RegisterSequence.FlowInit)
{
this.Class = Class;
}
/// <summary>
/// 注册顺序
/// </summary>
public RegisterSequence Class ;
}

View File

@@ -8,9 +8,20 @@ namespace Serein.Library
[AttributeUsage(AttributeTargets.Parameter)]
public class BindConvertorAttribute : Attribute
{
/// <summary>
/// 枚举类型
/// </summary>
public Type EnumType { get; }
/// <summary>
/// 转换器类型
/// </summary>
public Type ConvertorType { get; }
/// <summary>
/// 绑定转换器特性
/// </summary>
/// <param name="enum"></param>
/// <param name="convertor"></param>
public BindConvertorAttribute(Type @enum, Type convertor)
{
EnumType = @enum;

View File

@@ -2,11 +2,21 @@
namespace Serein.Library
{
/// <summary>
/// 绑定值特性
/// </summary>
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class BindValueAttribute : Attribute
{
/// <summary>
/// 绑定的值
/// </summary>
public object Value { get; }
/// <summary>
/// 绑定值特性构造函数
/// </summary>
/// <param name="value"></param>
public BindValueAttribute(object value)
{
Value = value;

View File

@@ -10,6 +10,11 @@ namespace Serein.Library
[AttributeUsage(AttributeTargets.Class)]
public sealed class DynamicFlowAttribute : Attribute
{
/// <summary>
/// 动态流程特性构造函数
/// </summary>
/// <param name="name"></param>
/// <param name="scan"></param>
public DynamicFlowAttribute(string name = "",bool scan = true)
{
Name = name;

View File

@@ -9,8 +9,16 @@ namespace Serein.Library
[AttributeUsage(AttributeTargets.Parameter)]
public class EnumTypeConvertorAttribute : Attribute
{
/// <summary>
/// 枚举类型
/// </summary>
public Type EnumType { get; }
/// <summary>
/// 枚举类型转换器特性构造函数
/// </summary>
/// <param name="enum"></param>
/// <exception cref="ArgumentException"></exception>
public EnumTypeConvertorAttribute(Type @enum)
{
if (@enum.IsEnum)

View File

@@ -9,6 +9,13 @@ namespace Serein.Library
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NodeActionAttribute : Attribute
{
/// <summary>
/// 节点行为特性构造函数
/// </summary>
/// <param name="methodDynamicType"></param>
/// <param name="methodTips"></param>
/// <param name="scan"></param>
/// <param name="lockName"></param>
public NodeActionAttribute(NodeType methodDynamicType,
string methodTips = "",
bool scan = true,

View File

@@ -7,7 +7,7 @@ namespace Serein.Library
/// <summary>
/// 表示了两个节点之间的连接关系,同时表示节点运行完成后,所会执行的下一个节点类型。
/// </summary
/// </summary>
public enum ConnectionInvokeType
{
/// <summary>

View File

@@ -6,9 +6,18 @@ using System.Threading.Tasks;
namespace Serein.Library
{
/// <summary>
/// 参数值输入类型
/// </summary>
public enum ParameterValueInputType
{
/// <summary>
/// 参数值输入类型 - 输入
/// </summary>
Input,
/// <summary>
/// 参数值输入类型 - 选择器(枚举类型)
/// </summary>
Select,
}
}

View File

@@ -8,6 +8,9 @@ namespace Serein.Library
/// </summary>
public class FlipflopException: Exception
{
/// <summary>
/// 触发器取消类型
/// </summary>
public enum CancelClass
{
/// <summary>
@@ -27,6 +30,13 @@ namespace Serein.Library
/// 取消类型
/// </summary>
public CancelClass Type { get; }
/// <summary>
/// 触发器异常构造函数
/// </summary>
/// <param name="message"></param>
/// <param name="isCancel"></param>
/// <param name="clsss"></param>
public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.CancelBranch) :base(message)
{
IsCancel = isCancel;

View File

@@ -11,10 +11,23 @@ namespace Serein.Library
[DynamicFlow(Name ="[基础功能]")]
public static class FlowBaseLibrary
{
/// <summary>
/// 对象透传,直接返回入参的值
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NodeAction(NodeType.Action, "对象透传")]
public static object TransmissionObject(object value) => value;
/// <summary>
/// 键值对组装,将入参的值与名称组装成一个字典对象
/// </summary>
/// <param name="argNames"></param>
/// <param name="value"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
[NodeAction(NodeType.Action, "键值对组装")]
public static Dictionary<string, object> DictSet(string argNames, params object[] value)
{
@@ -32,12 +45,22 @@ namespace Serein.Library
return dict;
}
/// <summary>
/// 数组组装,将入参的值组装成一个数组对象
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NodeAction(NodeType.Action, "数组组装")]
public static object[] ArraySet(params object[] value)
{
return value;
}
/// <summary>
/// 输出到控制台使用SereinEnv.WriteLine方法输出信息
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NodeAction(NodeType.Action, "输出")]
public static object[] Console(params object[] value)
{
@@ -48,7 +71,14 @@ namespace Serein.Library
return value;
}
[NodeAction(NodeType.Action, "逻辑分支")]
/// <summary>
/// 逻辑分支根据布尔值选择返回的值如果布尔值为true则返回t_value否则返回f_value
/// </summary>
/// <param name="bool"></param>
/// <param name="t_value"></param>
/// <param name="f_value"></param>
/// <returns></returns>
[NodeAction(NodeType.Action, "逻辑选择")]
public static object LogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
object t_value,
object f_value)
@@ -56,6 +86,12 @@ namespace Serein.Library
return @bool ? t_value : f_value;
}
/// <summary>
/// 文本拼接,将多个文本值拼接成一个字符串,支持换行符和制表符的特殊处理
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
[NodeAction(NodeType.Action, "文本拼接")]
public static string TextJoin(params object[] value)
{
@@ -79,6 +115,13 @@ namespace Serein.Library
return sb.ToString();
}
/// <summary>
/// 动态构建对象,将字典中的键值对转换为一个动态对象,支持指定类名和打印结果
/// </summary>
/// <param name="dict"></param>
/// <param name="classTypeName"></param>
/// <param name="IsPrint"></param>
/// <returns></returns>
[NodeAction(NodeType.Action, "键值对动态构建对象")]
public static object CreateDynamicObjectOfDict(Dictionary<string, object> dict,
@@ -100,13 +143,5 @@ namespace Serein.Library
return result;
}
[NodeAction(NodeType.Action, "设置或更新全局数据")]
public static object AddOrUpdateFlowGlobalData(string name, object data)
{
SereinEnv.AddOrUpdateFlowGlobalData(name, data);
return data;
}
}
}

View File

@@ -57,9 +57,18 @@ namespace Serein.Library
CollectionSetter
}
/// <summary>
/// 表示方法的类型
/// </summary>
public enum GSType
{
{
/// <summary>
/// 获取值
/// </summary>
Get,
/// <summary>
/// 设置值
/// </summary>
Set,
}
@@ -149,7 +158,7 @@ namespace Serein.Library
/// 目前提供了创建集合取值/赋值委托
/// </summary>
/// <param name="type">类型信息</param>
/// <param name="gsType">操作类型</param>
/// <param name="emitType">操作类型</param>
public DelegateDetails(Type type, EmitType emitType)
{
if (emitType == EmitType.CollectionSetter)

View File

@@ -5,6 +5,9 @@ using System.Threading.Tasks;
namespace Serein.Library
{
/// <summary>
/// FlipflopFunc 类提供了与 Flipflop 相关的功能方法。
/// </summary>
public static class FlipflopFunc
{
/// <summary>
@@ -72,15 +75,34 @@ namespace Serein.Library
/// </summary>
public class FlipflopContext<TResult> : IFlipflopContext<TResult>
{
/// <summary>
/// 触发器完成的状态(根据业务场景手动设置)
/// </summary>
public FlipflopStateType State { get; set; }
/// <summary>
/// 触发类型
/// </summary>
public TriggerDescription Type { get; set; }
/// <summary>
/// 触发时传递的数据
/// </summary>
public TResult Value { get; set; }
/// <summary>
/// 触发器上下文构造函数
/// </summary>
/// <param name="ffState"></param>
public FlipflopContext(FlipflopStateType ffState)
{
State = ffState;
}
/// <summary>
/// 触发器上下文构造函数,传入状态和数据值
/// </summary>
/// <param name="ffState"></param>
/// <param name="value"></param>
public FlipflopContext(FlipflopStateType ffState, TResult value)
{
State = ffState;

View File

@@ -16,81 +16,88 @@ namespace Serein.Library
/// <summary>
/// 流程画布
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node)]
public partial class FlowCanvasDetails
{
/// <summary>
/// 流程画布的构造函数
/// </summary>
/// <param name="env"></param>
public FlowCanvasDetails(IFlowEnvironment env)
{
Env = env;
}
/// <summary>
/// 流程画布的运行环境
/// </summary>
public IFlowEnvironment Env { get; }
/// <summary>
/// 画布拥有的节点
/// </summary>
[PropertyInfo(IsProtection = false)]
[DataInfo(IsProtection = false)]
private List<IFlowNode> _nodes = [];
//private System.Collections.ObjectModel.ObservableCollection<IFlowNode> _nodes = [];
/// <summary>
/// 画布公开的节点
/// </summary>
[PropertyInfo(IsProtection = false)]
[DataInfo(IsProtection = false)]
private List<IFlowNode> _publicNodes = [];
/// <summary>
/// 标识画布ID
/// </summary>
[PropertyInfo(IsProtection = false)]
[DataInfo(IsProtection = false)]
private string _guid;
/// <summary>
/// 画布名称
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string _name;
/// <summary>
/// 画布宽度
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private double _width;
/// <summary>
/// 画布高度
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private double _height;
/// <summary>
/// 预览位置X
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private double _viewX;
/// <summary>
/// 预览位置Y
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private double _viewY;
/// <summary>
/// 缩放比例X
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private double _scaleX = 1;
/// <summary>
/// 缩放比例Y
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private double _scaleY = 1;
/// <summary>
/// 起始节点
/// </summary>
[PropertyInfo]
[DataInfo]
private IFlowNode _startNode;
}

View File

@@ -21,7 +21,6 @@ namespace Serein.Library
/// 动态流程上下文
/// </summary>
/// <param name="flowEnvironment">脚本运行时的IOC</param>
/// <param name="ioc">脚本运行时使用的IOC容器</param>
public FlowContext(IFlowEnvironment flowEnvironment)
{
Env = flowEnvironment;
@@ -110,6 +109,10 @@ namespace Serein.Library
return flowInvokeInfo;
}
/// <summary>
/// 获取当前流程上下文的所有节点调用信息,包含每个节点的执行时间、调用类型、执行状态等。
/// </summary>
/// <returns></returns>
public List<FlowInvokeInfo> GetAllInvokeInfos() => [.. flowInvokeInfos.Values];
/// <summary>

View File

@@ -9,17 +9,6 @@ using System.Threading.Tasks;
namespace Serein.Library
{
/// <summary>
/// 表示空数据
/// </summary>
/*public readonly struct Unit : IEquatable<Unit>
{
public static readonly Unit Default = default;
public bool Equals(Unit _) => true;
public override bool Equals(object obj) => obj is Unit;
public override int GetHashCode() => 0;
}*/
/// <summary>
/// 流程返回值的包装

View File

@@ -33,6 +33,11 @@ namespace Serein.Library.FlowNode
/// </summary>
public class JunctionModel
{
/// <summary>
/// 连接点模型构造函数
/// </summary>
/// <param name="NodeModel"></param>
/// <param name="JunctionType"></param>
public JunctionModel(IFlowNode NodeModel, JunctionType JunctionType)
{
Guid = System.Guid.NewGuid().ToString();

View File

@@ -1,5 +1,4 @@
using Microsoft.Extensions.ObjectPool;
using Serein.Library.Api;
using Serein.Library.Api;
using Serein.Library.Utils;
using System;
using System.Collections.Generic;
@@ -8,76 +7,21 @@ using System.Threading.Tasks;
namespace Serein.Library
{
/*
public class CallNodeLookup : IFlowCallTree
{
private static readonly string[] _keys = new[]
{
"Start", // 0
"Stop", // 1
"Reset", // 2
"Pause", // 3
"Resume", // 4
"Check", // 5
"Init", // 6
"Load", // 7
"Save", // 8
"Clear" // 9
};
private static readonly CallNode[] _values = new CallNode[10];
static CallNodeLookup()
{
*//*_values[0] = new CallNode("Start");
_values[1] = new CallNode("Stop");
_values[2] = new CallNode("Reset");
_values[3] = new CallNode("Pause");
_values[4] = new CallNode("Resume");
_values[5] = new CallNode("Check");
_values[6] = new CallNode("Init");
_values[7] = new CallNode("Load");
_values[8] = new CallNode("Save");
_values[9] = new CallNode("Clear");*//*
}
// 最小冲突哈希函数(简单示例,固定键集有效)
private static int PerfectHash(string key)
{
return key switch
{
"Start" => 0,
"Stop" => 1,
"Reset" => 2,
"Pause" => 3,
"Resume" => 4,
"Check" => 5,
"Init" => 6,
"Load" => 7,
"Save" => 8,
"Clear" => 9,
_ => -1
};
}
public CallNode Get(string key)
{
int index = PerfectHash(key);
if (index >= 0 && _keys[index] == key)
return _values[index];
return null;
}
}
*/
/// <summary>
/// 流程调用树,管理所有的调用节点
/// </summary>
public class FlowCallTree : IFlowCallTree
{
private readonly SortedDictionary<string, CallNode> _callNodes = new SortedDictionary<string,CallNode>();
//private readonly Dictionary<string, CallNode> _callNodes = new Dictionary<string,CallNode>();
/// <summary>
/// 索引器允许通过字符串索引访问CallNode
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public CallNode this[string index]
{
get
@@ -92,17 +36,33 @@ namespace Serein.Library
}
}
/// <summary>
/// 添加一个调用节点到流程调用树中
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="action"></param>
public void AddCallNode(string nodeGuid, Action<IFlowContext> action)
{
var node = new CallNode(nodeGuid, action);
_callNodes[nodeGuid] = node;
}
/// <summary>
/// 添加一个调用节点到流程调用树中,使用异步函数
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="func"></param>
public void AddCallNode(string nodeGuid, Func<IFlowContext, Task> func)
{
var node = new CallNode(nodeGuid, func);
_callNodes[nodeGuid] = node;
}
/// <summary>
/// 获取指定Key的CallNode如果不存在则返回null
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public CallNode Get(string key)
{
return _callNodes.TryGetValue(key, out CallNode callNode) ? callNode : null;
@@ -112,7 +72,9 @@ namespace Serein.Library
/// <summary>
/// 调用节点代表一个流程中的调用点可以是一个Action或一个异步函数。
/// </summary>
public class CallNode
{
@@ -120,11 +82,21 @@ namespace Serein.Library
private Func<IFlowContext, Task> taskFunc;
private Action<IFlowContext> action;
/// <summary>
/// 创建一个新的调用节点使用指定的节点Guid。
/// </summary>
/// <param name="nodeGuid"></param>
public CallNode(string nodeGuid)
{
Guid = nodeGuid;
Init();
}
/// <summary>
/// 创建一个新的调用节点使用指定的节点Guid和Action。
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="action"></param>
public CallNode(string nodeGuid, Action<IFlowContext> action)
{
Guid = nodeGuid;
@@ -132,6 +104,11 @@ namespace Serein.Library
Init();
}
/// <summary>
/// 创建一个新的调用节点使用指定的节点Guid和异步函数。
/// </summary>
/// <param name="nodeGuid"></param>
/// <param name="func"></param>
public CallNode(string nodeGuid, Func<IFlowContext, Task> func)
{
Guid = nodeGuid;
@@ -139,7 +116,9 @@ namespace Serein.Library
Init();
}
/// <summary>
/// 初始化调用节点,设置默认的子节点和后继节点字典。
/// </summary>
private void Init()
{
//PreviousNodes = new Dictionary<ConnectionInvokeType, List<CallNode>>();
@@ -151,17 +130,28 @@ namespace Serein.Library
}
}
private enum ActionType
{
Action,
Task,
}
private ActionType actionType = ActionType.Action;
/// <summary>
/// 设置调用节点的Action表示该节点执行一个同步操作。
/// </summary>
/// <param name="action"></param>
public void SetAction(Action<IFlowContext> action)
{
this.action = action;
actionType = ActionType.Action;
}
/// <summary>
/// 设置调用节点的异步函数,表示该节点执行一个异步操作。
/// </summary>
/// <param name="taskFunc"></param>
public void SetAction(Func<IFlowContext, Task> taskFunc)
{
this.taskFunc = taskFunc;
@@ -173,15 +163,23 @@ namespace Serein.Library
/// 对应的节点
/// </summary>
public string Guid { get; }
#if false
/// <summary>
/// 不同分支的父节点(流程调用)
/// </summary>
//public Dictionary<ConnectionInvokeType, List<CallNode>> PreviousNodes { get; private set; }
public Dictionary<ConnectionInvokeType, List<CallNode>> PreviousNodes { get; private set; }
#endif
/// <summary>
/// 不同分支的子节点(流程调用)
/// </summary>
public Dictionary<ConnectionInvokeType, List<CallNode>> SuccessorNodes { get; private set; }
/// <summary>
/// 子节点数组分为四个分支上游、成功、失败、错误每个分支最多支持16个子节点。
/// </summary>
public CallNode[][] ChildNodes { get; private set; } = new CallNode[][]
{
new CallNode[MaxChildNodeCount],
@@ -191,7 +189,12 @@ namespace Serein.Library
};
private const int MaxChildNodeCount = 16; // 每个分支最多支持16个子节点
/// <summary>
/// 获取指定类型的子节点数量。
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public int GetCount(ConnectionInvokeType type)
{
if (type == ConnectionInvokeType.Upstream) return UpstreamNodeCount;
@@ -201,11 +204,29 @@ namespace Serein.Library
return 0;
}
/// <summary>
/// 获取当前节点的子节点数量。
/// </summary>
public int UpstreamNodeCount { get; private set; } = 0;
/// <summary>
/// 获取当前节点的成功后继子节点数量。
/// </summary>
public int IsSuccessorNodeCount { get; private set; } = 0;
/// <summary>
/// 获取当前节点的失败后继子节点数量。
/// </summary>
public int IsFailNodeCount { get; private set; } = 0;
/// <summary>
/// 获取当前节点的错误后继子节点数量。
/// </summary>
public int IsErrorNodeCount { get; private set; } = 0;
/// <summary>
/// 添加一个上游子节点到当前节点。
/// </summary>
/// <param name="callNode"></param>
/// <returns></returns>
public CallNode AddChildNodeUpstream(CallNode callNode)
{
var connectionInvokeType = ConnectionInvokeType.Upstream;
@@ -214,6 +235,11 @@ namespace Serein.Library
return this;
}
/// <summary>
/// 添加一个成功后继子节点到当前节点。
/// </summary>
/// <param name="callNode"></param>
/// <returns></returns>
public CallNode AddChildNodeSucceed(CallNode callNode)
{
ChildNodes[0][UpstreamNodeCount++] = callNode;
@@ -224,6 +250,11 @@ namespace Serein.Library
return this;
}
/// <summary>
/// 添加一个失败后继子节点到当前节点。
/// </summary>
/// <param name="callNode"></param>
/// <returns></returns>
public CallNode AddChildNodeFail(CallNode callNode)
{
var connectionInvokeType = ConnectionInvokeType.IsFail;
@@ -232,6 +263,12 @@ namespace Serein.Library
return this;
}
/// <summary>
/// 添加一个错误后继子节点到当前节点。
/// </summary>
/// <param name="callNode"></param>
/// <returns></returns>
public CallNode AddChildNodeError(CallNode callNode)
{
var connectionInvokeType = ConnectionInvokeType.IsError;
@@ -268,7 +305,7 @@ namespace Serein.Library
}
}
private static readonly DefaultObjectPool<Stack<CallNode>> _stackPool = new DefaultObjectPool<Stack<CallNode>>(new DefaultPooledObjectPolicy<Stack<CallNode>>());
private static readonly ObjectPool<Stack<CallNode>> _stackPool = new ObjectPool<Stack<CallNode>>(() => new Stack<CallNode>());
/// <summary>
@@ -279,7 +316,7 @@ namespace Serein.Library
/// <returns></returns>
public async Task<FlowResult> StartFlowAsync(IFlowContext context, CancellationToken token)
{
var stack = _stackPool.Get();
var stack = _stackPool.Allocate();
stack.Push(this);
while (true)
{
@@ -331,7 +368,7 @@ namespace Serein.Library
if (stack.Count == 0)
{
_stackPool.Return(stack);
_stackPool.Free(stack);
flowResult = context.GetFlowData(currentNode.Guid);
return flowResult; // 说明流程到了终点
}
@@ -339,7 +376,7 @@ namespace Serein.Library
if (context.RunState == RunState.Completion)
{
_stackPool.Return(stack);
_stackPool.Free(stack);
context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
flowResult = context.GetFlowData(currentNode.Guid);
return flowResult; // 流程执行完成,返回结果
@@ -347,7 +384,7 @@ namespace Serein.Library
if (token.IsCancellationRequested)
{
_stackPool.Return(stack);
_stackPool.Free(stack);
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
}
@@ -359,8 +396,16 @@ namespace Serein.Library
}
/// <summary>
/// 流程调用树接口提供获取CallNode的方法。
/// </summary>
public interface IFlowCallTree
{
/// <summary>
/// 获取指定Key的CallNode如果不存在则返回null。
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
CallNode Get(string key);
}
@@ -371,10 +416,22 @@ namespace Serein.Library
{
private readonly IFlowCallTree flowCallTree;
private readonly IFlowEnvironment flowEnvironment;
/// <summary>
/// 轻量级流程上下文池,使用对象池模式来管理流程上下文的创建和回收。
/// </summary>
public static Serein.Library.Utils.ObjectPool<IFlowContext> FlowContextPool { get; set; }
/// <summary>
/// 单例IOC容器用于依赖注入和服务定位。
/// </summary>
public ISereinIOC IOC => throw new NotImplementedException();
/// <summary>
/// 轻量级流程控制器构造函数,接受流程调用树和流程环境作为参数。
/// </summary>
/// <param name="flowCallTree"></param>
/// <param name="flowEnvironment"></param>
public LightweightFlowControl(IFlowCallTree flowCallTree, IFlowEnvironment flowEnvironment)
{
this.flowCallTree = flowCallTree;
@@ -385,11 +442,12 @@ namespace Serein.Library
});
}
/// <inheritdoc/>
public Task<object> InvokeAsync(string apiGuid, Dictionary<string, object> dict)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public Task<TResult> InvokeAsync<TResult>(string apiGuid, Dictionary<string, object> dict)
{
throw new NotImplementedException();
@@ -398,7 +456,7 @@ namespace Serein.Library
//private readonly DefaultObjectPool<IDynamicContext> _stackPool = new DefaultObjectPool<IDynamicContext>(new DynamicContext(this));
/// <inheritdoc/>
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
{
IFlowContext context = Serein.Library.LightweightFlowControl.FlowContextPool.Allocate();
@@ -443,45 +501,48 @@ namespace Serein.Library
throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。");
}
}
public async Task<bool> StartFlowAsync(string[] canvasGuids)
/// <inheritdoc/>
public Task<bool> StartFlowAsync(string[] canvasGuids)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public Task<bool> ExitFlowAsync()
{
throw new NotImplementedException();
}
#region
/// <inheritdoc/>
public void ActivateFlipflopNode(string nodeGuid)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void TerminateFlipflopNode(string nodeGuid)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void UseExternalIOC(ISereinIOC ioc)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC> setDefultMemberOnReset = null)
{
throw new NotImplementedException();
@@ -495,110 +556,128 @@ namespace Serein.Library
/// </summary>
public class LightweightFlowEnvironmentEvent : IFlowEnvironmentEvent
{
/// <inheritdoc/>
public event LoadDllHandler DllLoad;
/// <inheritdoc/>
public event ProjectLoadedHandler ProjectLoaded;
/// <inheritdoc/>
public event ProjectSavingHandler ProjectSaving;
/// <inheritdoc/>
public event NodeConnectChangeHandler NodeConnectChanged;
/// <inheritdoc/>
public event CanvasCreateHandler CanvasCreated;
/// <inheritdoc/>
public event CanvasRemoveHandler CanvasRemoved;
/// <inheritdoc/>
public event NodeCreateHandler NodeCreated;
/// <inheritdoc/>
public event NodeRemoveHandler NodeRemoved;
/// <inheritdoc/>
public event NodePlaceHandler NodePlace;
/// <inheritdoc/>
public event NodeTakeOutHandler NodeTakeOut;
/// <inheritdoc/>
public event StartNodeChangeHandler StartNodeChanged;
/// <inheritdoc/>
public event FlowRunCompleteHandler FlowRunComplete;
/// <inheritdoc/>
public event MonitorObjectChangeHandler MonitorObjectChanged;
/// <inheritdoc/>
public event NodeInterruptStateChangeHandler NodeInterruptStateChanged;
/// <inheritdoc/>
public event ExpInterruptTriggerHandler InterruptTriggered;
/// <inheritdoc/>
public event IOCMembersChangedHandler IOCMembersChanged;
/// <inheritdoc/>
public event NodeLocatedHandler NodeLocated;
/// <inheritdoc/>
public event EnvOutHandler EnvOutput;
/// <inheritdoc/>
public void OnDllLoad(LoadDllEventArgs eventArgs)
{
DllLoad?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
{
ProjectLoaded?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnProjectSaving(ProjectSavingEventArgs eventArgs)
{
ProjectSaving?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs)
{
NodeConnectChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnCanvasCreated(CanvasCreateEventArgs eventArgs)
{
CanvasCreated?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs)
{
CanvasRemoved?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeCreated(NodeCreateEventArgs eventArgs)
{
NodeCreated?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeRemoved(NodeRemoveEventArgs eventArgs)
{
NodeRemoved?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodePlace(NodePlaceEventArgs eventArgs)
{
NodePlace?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
{
NodeTakeOut?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs)
{
StartNodeChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnFlowRunComplete(FlowEventArgs eventArgs)
{
FlowRunComplete?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs)
{
MonitorObjectChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs)
{
NodeInterruptStateChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs)
{
InterruptTriggered?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs)
{
IOCMembersChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeLocated(NodeLocatedEventArgs eventArgs)
{
NodeLocated?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnEnvOutput(InfoType type, string value)
{
EnvOutput?.Invoke(type, value);
@@ -611,128 +690,131 @@ namespace Serein.Library
/// </summary>
public class LightweightFlowEnvironment : IFlowEnvironment
{
/// <summary>
/// 轻量级流程环境构造函数,接受一个流程环境事件接口。
/// </summary>
/// <param name="lightweightFlowEnvironmentEvent"></param>
public LightweightFlowEnvironment(IFlowEnvironmentEvent lightweightFlowEnvironmentEvent)
{
this.Event = lightweightFlowEnvironmentEvent;
}
/// <inheritdoc/>
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial)
{
Console.WriteLine(message);
}
/// <inheritdoc/>
public ISereinIOC IOC => throw new NotImplementedException();
/// <inheritdoc/>
public IFlowEdit FlowEdit => throw new NotImplementedException();
/// <inheritdoc/>
public IFlowControl FlowControl => throw new NotImplementedException();
/// <inheritdoc/>
public IFlowEnvironmentEvent Event { get; private set; }
/// <inheritdoc/>
public string EnvName => throw new NotImplementedException();
/// <inheritdoc/>
public string ProjectFileLocation => throw new NotImplementedException();
/// <inheritdoc/>
public bool _IsGlobalInterrupt => throw new NotImplementedException();
/// <inheritdoc/>
public bool IsControlRemoteEnv => throw new NotImplementedException();
/// <inheritdoc/>
public InfoClass InfoClass { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <inheritdoc/>
public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); }
/// <inheritdoc/>
public IFlowEnvironment CurrentEnv => throw new NotImplementedException();
/// <inheritdoc/>
public UIContextOperation UIContextOperation => throw new NotImplementedException();
/* public Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
throw new NotImplementedException();
}*/
/* public Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
throw new NotImplementedException();
}*/
/// <inheritdoc/>
public void ExitRemoteEnv()
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public Task<FlowEnvInfo> GetEnvInfoAsync()
{
throw new NotImplementedException();
}
public Task<SereinProjectData> GetProjectInfoAsync()
/// <inheritdoc/>
public SereinProjectData GetProjectInfoAsync()
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void LoadLibrary(string dllPath)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public bool LoadNativeLibraryOfRuning(string file)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void LoadProject(string filePath)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public Task LoadProjetAsync(string filePath)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void SaveProject()
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void SetUIContextOperation(UIContextOperation uiContextOperation)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public Task StartRemoteServerAsync(int port = 7525)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public void StopRemoteServer()
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
{
throw new NotImplementedException();
}
/// <inheritdoc/>
public bool TryUnloadLibrary(string assemblyFullName)
{
throw new NotImplementedException();

View File

@@ -12,7 +12,7 @@ namespace Serein.Library
/// <summary>
/// 每个节点有独自的MethodDetails实例
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Method)]
[FlowDataProperty(ValuePath = NodeValuePath.Method)]
public partial class MethodDetails
{
// private readonly IFlowEnvironment env;
@@ -20,57 +20,51 @@ namespace Serein.Library
/// <summary>
/// 对应的节点
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private IFlowNode _nodeModel;
/// <summary>
/// 对应的程序集
/// </summary>
[PropertyInfo]
[DataInfo]
private string _assemblyName;
/// <summary>
/// 调用节点方法时需要的实例(多个相同的节点将拥有相同的类型)
/// </summary>
[PropertyInfo]
[DataInfo]
private Type _actingInstanceType;
/// <summary>
/// 作用实例(多个相同的节点将会共享同一个实例)
/// </summary>
// [PropertyInfo]
// private object _actingInstance;
/// <summary>
/// 方法名称
/// </summary>
[PropertyInfo]
[DataInfo]
private string _methodName;
/// <summary>
/// 节点类型
/// </summary>
[PropertyInfo]
[DataInfo]
private NodeType _methodDynamicType;
/// <summary>
/// 锁名称(暂未实现)
/// </summary>
[PropertyInfo]
[DataInfo]
private string _methodLockName;
/// <summary>
/// 方法别名
/// </summary>
[PropertyInfo]
[DataInfo]
private string _methodAnotherName;
/// <summary>
/// 参数描述
/// </summary>
[PropertyInfo]
[DataInfo]
private ParameterDetails[] _parameterDetailss;
/// <summary>
@@ -78,19 +72,19 @@ namespace Serein.Library
/// <para>-1表示不存在</para>
/// <para>0表示第一个参数是可选参数</para>
/// </summary>
[PropertyInfo]
[DataInfo]
private int _paramsArgIndex = -1;
/// <summary>
/// 是否为异步方法如果为异步方法则返回值类型为Task或Task&lt;T&gt;
/// </summary>
[PropertyInfo]
[DataInfo]
private bool _isAsync = false;
/// <summary>
/// 出参类型
/// </summary>
[PropertyInfo]
[DataInfo]
private Type _returnType;
}
@@ -133,7 +127,11 @@ namespace Serein.Library
return true;
}
/// <summary>
/// 新增可变参数
/// </summary>
/// <param name="parameterDetails"></param>
/// <returns></returns>
public bool AddParamsArg(ParameterDetails parameterDetails)
{
if (ParamsArgIndex < 0) // 方法是否包含可变参数
@@ -313,6 +311,10 @@ namespace Serein.Library
return md;
}
/// <summary>
/// 将方法信息转换为字符串,方便调试和查看
/// </summary>
/// <returns></returns>
public override string ToString()
{
if (string.IsNullOrEmpty(this.MethodName))

View File

@@ -8,7 +8,7 @@ namespace Serein.Library
/// <summary>
/// 节点调试设置,用于中断节点的运行
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.DebugSetting)]
[FlowDataProperty(ValuePath = NodeValuePath.DebugSetting)]
public partial class NodeDebugSetting
{
/// <summary>
@@ -24,25 +24,25 @@ namespace Serein.Library
/// <summary>
/// 是否保护参数
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private bool _isProtectionParameter = false;
/// <summary>
/// 对应的节点
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private IFlowNode _nodeModel;
/// <summary>
/// 是否使能
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private bool _isEnable = true;
/// <summary>
/// 是否中断节点。
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private bool _isInterrupt = false;
}

View File

@@ -36,6 +36,11 @@ namespace Serein.Library
/// </summary>
public Type ViewModelType { get; set; }
/// <summary>
/// 节点类型信息字符串表示
/// </summary>
/// <returns></returns>
public override string ToString()
{
return $"$[{NodeType}]类型信息 : ModelType->{ModelType};ControlType->{ControlType};ViewModelType->{ViewModelType}";

View File

@@ -14,7 +14,7 @@ namespace Serein.Library
/// <summary>
/// 节点入参参数详情
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Parameter)]
[FlowDataProperty(ValuePath = NodeValuePath.Parameter)]
public partial class ParameterDetails
{
// private readonly IFlowEnvironment env;
@@ -22,13 +22,13 @@ namespace Serein.Library
/// <summary>
/// 所在的节点
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private IFlowNode _nodeModel;
/// <summary>
/// 参数索引
/// </summary>
[PropertyInfo]
[DataInfo]
private int _index;
/// <summary>
@@ -36,7 +36,7 @@ namespace Serein.Library
/// <para>如果为 true ,则使用输入的文本值作为入参数据。</para>
/// <para>如果为 false ,则在当前流程上下文中,根据 ArgDataSourceNodeGuid 查找到对应节点,并根据 ArgDataSourceNodeGuid 判断如何获取其返回的数据,以此作为入参数据。</para>
/// </summary>
[PropertyInfo(IsNotification = true, IsVerify = true)]
[DataInfo(IsNotification = true, IsVerify = true)]
private bool _isExplicitData ;
///// <summary>
@@ -49,7 +49,7 @@ namespace Serein.Library
/// 方法入参若无相关转换器特性标注则无需关注该变量。该变量用于需要用到枚举BinValue转换器时指示相应的入参变量需要转为的类型。
/// </summary>
[Obsolete("转换器特性将在下一个大版本中移除")]
[PropertyInfo]
[DataInfo]
private Type _explicitType ;
/// <summary>
@@ -58,56 +58,56 @@ namespace Serein.Library
/// <para>Bool : 布尔类型</para>
/// <para>Value :除以上类型之外的任意参数</para>
/// </summary>
[PropertyInfo]
[DataInfo]
private ParameterValueInputType _inputType ;
/// <summary>
/// 入参数据来源。默认使用上一节点作为入参数据。
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private ConnectionArgSourceType _argDataSourceType = ConnectionArgSourceType.GetPreviousNodeData;
/// <summary>
/// 当 ArgDataSourceType 不为 GetPreviousNodeData 时(从运行时上一节点获取数据)。
/// 则通过当前上下文获取该Guid对应的数据作为预处理的入参参数。
/// </summary>
[PropertyInfo]
[DataInfo]
private string _argDataSourceNodeGuid;
/// <summary>
/// 方法入参需要的类型。
/// </summary>
[PropertyInfo]
[DataInfo]
private Type _dataType ;
/// <summary>
/// 方法入参参数名称
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string _name ;
/// <summary>
/// 入参注释
/// </summary>
[PropertyInfo]
[DataInfo]
private string _description;
/// <summary>
/// 自定义的方法入参数据
/// </summary>
[PropertyInfo(IsNotification = true)] // IsPrint = true
[DataInfo(IsNotification = true)] // IsPrint = true
private string _dataValue;
/// <summary>
/// 只有当 InputType 为 Select 时,才会需要该成员。
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string[] _items ;
/// <summary>
/// 指示该属性是可变参数的其中一员(可变参数为数组类型)
/// </summary>
[PropertyInfo]
[DataInfo]
private bool _isParams;
}
@@ -134,6 +134,11 @@ namespace Serein.Library
this.NodeModel = nodeModel;
}
/// <summary>
/// 通过参数数据加载实体,用于加载项目文件、远程连接的场景
/// </summary>
/// <param name="pdInfo"></param>
/// <param name="argIndex"></param>
public ParameterDetails(ParameterData pdInfo, int argIndex)
{
this.Index = argIndex;
@@ -163,6 +168,7 @@ namespace Serein.Library
IsParams = info.IsParams;
}
partial void BeforeTheIsExplicitData(ref bool __isAllow, bool newValue)
{
if(DataType == typeof(IFlowContext))
@@ -214,6 +220,12 @@ namespace Serein.Library
return pd;
}
/// <summary>
/// 转为方法入参数据
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public async Task<object> ToMethodArgData(IFlowContext context)
{
// 1. 从缓存获取
@@ -310,177 +322,181 @@ namespace Serein.Library
/* /// <summary>
/// 转为方法入参数据
/* /// <summary>
/// 转为方法入参数据
/// </summary>
/// <returns></returns>
public async Task<object> ToMethodArgData2(IFlowContext context)
{
var nodeModel = NodeModel;
var env = nodeModel.Env;
#region 流程运行上下文预设的参数
if (context.TryGetParamsTempData(NodeModel.Guid, Index, out var data))
{
return data;
}
#endregion
#region 显然的流程基本类型
// 返回运行环境
if (typeof(IFlowEnvironment).IsAssignableFrom(DataType))
{
return env;
}
// 返回流程上下文
if (typeof(IFlowContext).IsAssignableFrom(DataType))
{
return context;
}
// 返回流程上下文
if (typeof(IFlowNode).IsAssignableFrom(DataType))
{
return NodeModel;
}
// 显式设置的参数
if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase))
{
return DataValue.ToConvertValueType(DataType); // 并非表达式,同时是显式设置的参数
}
#endregion
*//*#region “枚举-类型”转换器
if (ExplicitType is not null && ExplicitType.IsEnum && DataType != ExplicitType)
{
var resultEnum = Enum.Parse(ExplicitType, DataValue);
// 获取绑定的类型
var type = EnumHelper.GetBoundValue(ExplicitType, resultEnum, attr => attr.Value);
if (type is Type enumBindType && !(enumBindType is null))
{
var value = nodeModel.Env.IOC.CreateObject(enumBindType);
return value;
}
}
#endregion*//*
// 需要获取预入参数据
object inputParameter;
#region (默认的)从运行时上游节点获取其返回值
if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
var previousNode = context.GetPreviousNode(nodeModel.Guid);
if (previousNode is null)
{
inputParameter = null;
}
else
{
var flowData = context.GetFlowData(previousNode);
inputParameter = flowData.Value; // 当前传递的数据
}
}
else
{
if(!env.TryGetNodeModel(ArgDataSourceNodeGuid, out var argSourceNodeModel))
{
throw new Exception($"[arg{Index}][{Name}][{DataType}]需要节点[{ArgDataSourceNodeGuid}]的参数,但节点不存在");
}
// 如果是公开的节点,需要判断上下文调用中是否存在流程接口节点
if (argSourceNodeModel.IsPublic)
{
var pnGuid = context.GetPreviousNode(NodeModel.Guid);
var pn = env.TryGetNodeModel(pnGuid, out var tmpNode) ? tmpNode : null;
if (pn.ControlType == NodeControlType.FlowCall)
{
argSourceNodeModel = pn;
}
}
if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
{
var flowData = context.GetFlowData(argSourceNodeModel.Guid);
if(flowData is null)
{
inputParameter = null;
}
else
{
inputParameter = flowData.Value;
}
}
else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
{
// 立刻调用对应节点获取数据。
var cts = new CancellationTokenSource();
var result = await argSourceNodeModel.ExecutingAsync(context, cts.Token);
cts?.Cancel();
cts?.Dispose();
inputParameter = result.Value;
}
else
{
throw new Exception("节点执行方法获取入参参数时ConnectionArgSourceType枚举是意外的枚举值");
}
}
#endregion
#region 判断是否执行表达式
if (IsExplicitData)
{
// @Get 表达式 (从上一节点获取对象)
if (DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase))
{
inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _);
}
// @DTC 表达式 Data type conversion
else if (DataValue.StartsWith("@dtc", StringComparison.OrdinalIgnoreCase))
{
inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _);
}
// @Data 表达式 (获取全局数据)
else if (DataValue.StartsWith("@data", StringComparison.OrdinalIgnoreCase))
{
inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _);
}
}
#endregion
// 对引用类型检查 null
if (!DataType.IsValueType && inputParameter is null)
{
throw new Exception($"[arg{Index}][{Name}][{DataType}]参数不能为null");
}
if (DataType == typeof(string)) // 转为字符串
{
return inputParameter.ToString();
}
var inputParameterType = inputParameter.GetType();
if (DataType.IsSubclassOf(inputParameterType)) // 入参类型 是 预入参数据类型 的 子类/实现类
{
// 方法入参中,父类不能隐式转为子类,这里需要进行强制转换
return ObjectConvertHelper.ConvertParentToChild(inputParameter, DataType);
}
if (DataType.IsAssignableFrom(inputParameterType)) // 入参类型 是 预入参数据类型 的 父类/接口
{
return inputParameter;
}
throw new Exception($"[arg{Index}][{Name}][{DataType}]入参类型不符合,当前预入参类型为{inputParameterType}");
}
*/
/// <summary>
/// 转为字符串描述
/// </summary>
/// <returns></returns>
public async Task<object> ToMethodArgData2(IFlowContext context)
{
var nodeModel = NodeModel;
var env = nodeModel.Env;
#region 流程运行上下文预设的参数
if (context.TryGetParamsTempData(NodeModel.Guid, Index, out var data))
{
return data;
}
#endregion
#region 显然的流程基本类型
// 返回运行环境
if (typeof(IFlowEnvironment).IsAssignableFrom(DataType))
{
return env;
}
// 返回流程上下文
if (typeof(IFlowContext).IsAssignableFrom(DataType))
{
return context;
}
// 返回流程上下文
if (typeof(IFlowNode).IsAssignableFrom(DataType))
{
return NodeModel;
}
// 显式设置的参数
if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase))
{
return DataValue.ToConvertValueType(DataType); // 并非表达式,同时是显式设置的参数
}
#endregion
*//*#region “枚举-类型”转换器
if (ExplicitType is not null && ExplicitType.IsEnum && DataType != ExplicitType)
{
var resultEnum = Enum.Parse(ExplicitType, DataValue);
// 获取绑定的类型
var type = EnumHelper.GetBoundValue(ExplicitType, resultEnum, attr => attr.Value);
if (type is Type enumBindType && !(enumBindType is null))
{
var value = nodeModel.Env.IOC.CreateObject(enumBindType);
return value;
}
}
#endregion*//*
// 需要获取预入参数据
object inputParameter;
#region (默认的)从运行时上游节点获取其返回值
if (ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
{
var previousNode = context.GetPreviousNode(nodeModel.Guid);
if (previousNode is null)
{
inputParameter = null;
}
else
{
var flowData = context.GetFlowData(previousNode);
inputParameter = flowData.Value; // 当前传递的数据
}
}
else
{
if(!env.TryGetNodeModel(ArgDataSourceNodeGuid, out var argSourceNodeModel))
{
throw new Exception($"[arg{Index}][{Name}][{DataType}]需要节点[{ArgDataSourceNodeGuid}]的参数,但节点不存在");
}
// 如果是公开的节点,需要判断上下文调用中是否存在流程接口节点
if (argSourceNodeModel.IsPublic)
{
var pnGuid = context.GetPreviousNode(NodeModel.Guid);
var pn = env.TryGetNodeModel(pnGuid, out var tmpNode) ? tmpNode : null;
if (pn.ControlType == NodeControlType.FlowCall)
{
argSourceNodeModel = pn;
}
}
if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
{
var flowData = context.GetFlowData(argSourceNodeModel.Guid);
if(flowData is null)
{
inputParameter = null;
}
else
{
inputParameter = flowData.Value;
}
}
else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
{
// 立刻调用对应节点获取数据。
var cts = new CancellationTokenSource();
var result = await argSourceNodeModel.ExecutingAsync(context, cts.Token);
cts?.Cancel();
cts?.Dispose();
inputParameter = result.Value;
}
else
{
throw new Exception("节点执行方法获取入参参数时ConnectionArgSourceType枚举是意外的枚举值");
}
}
#endregion
#region 判断是否执行表达式
if (IsExplicitData)
{
// @Get 表达式 (从上一节点获取对象)
if (DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase))
{
inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _);
}
// @DTC 表达式 Data type conversion
else if (DataValue.StartsWith("@dtc", StringComparison.OrdinalIgnoreCase))
{
inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _);
}
// @Data 表达式 (获取全局数据)
else if (DataValue.StartsWith("@data", StringComparison.OrdinalIgnoreCase))
{
inputParameter = SerinExpressionEvaluator.Evaluate(DataValue, inputParameter, out _);
}
}
#endregion
// 对引用类型检查 null
if (!DataType.IsValueType && inputParameter is null)
{
throw new Exception($"[arg{Index}][{Name}][{DataType}]参数不能为null");
}
if (DataType == typeof(string)) // 转为字符串
{
return inputParameter.ToString();
}
var inputParameterType = inputParameter.GetType();
if (DataType.IsSubclassOf(inputParameterType)) // 入参类型 是 预入参数据类型 的 子类/实现类
{
// 方法入参中,父类不能隐式转为子类,这里需要进行强制转换
return ObjectConvertHelper.ConvertParentToChild(inputParameter, DataType);
}
if (DataType.IsAssignableFrom(inputParameterType)) // 入参类型 是 预入参数据类型 的 父类/接口
{
return inputParameter;
}
throw new Exception($"[arg{Index}][{Name}][{DataType}]入参类型不符合,当前预入参类型为{inputParameterType}");
}
*/
public override string ToString()
{
return $"[{this.Index}] {(string.IsNullOrWhiteSpace(this.Description) ? string.Empty : $"({this.Description})")}{this.Name} : {this.DataType?.FullName}";

View File

@@ -270,7 +270,7 @@ namespace Serein.Library
/// <summary>
/// 节点于画布中的位置(通用类)
/// </summary>
[NodeProperty]
[FlowDataProperty]
public partial class PositionOfUI
{
/// <summary>
@@ -285,13 +285,13 @@ namespace Serein.Library
/// <summary>
/// 指示控件在画布的横向向方向上的位置
/// </summary>
[PropertyInfo]
[DataInfo]
private double _x = 0;
/// <summary>
/// 指示控件在画布的纵向方向上的位置
/// </summary>
[PropertyInfo]
[DataInfo]
private double _y = 0;
}

View File

@@ -0,0 +1,8 @@
{
"profiles": {
"Serein.Library": {
"commandName": "Project",
"remoteDebugEnabled": false
}
}
}

View File

@@ -8,35 +8,83 @@ namespace Serein.Library
{
/// <summary>
/// 脚本代码中常用的函数
/// </summary>
public static class ScriptBaseFunc
{
/// <summary>
/// 获取当前时间
/// </summary>
/// <returns></returns>
public static DateTime now() => DateTime.Now;
#region
/// <summary>
/// 将值转换为bool类型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static bool @bool(object value)
{
return ConvertHelper.ValueParse<bool>(value);
}
/// <summary>
/// 将值转换为字节类型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static byte @byte(object value)
{
return ConvertHelper.ValueParse<byte>(value);
}
/// <summary>
/// 将值转换为短整型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static decimal @decimal(object value)
{
return ConvertHelper.ValueParse<decimal>(value);
}
/// <summary>
/// 将值转换为浮点型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static float @float(object value)
{
return ConvertHelper.ValueParse<float>(value);
}
/// <summary>
/// 将值转换为双精度浮点型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static double @double(object value)
{
return ConvertHelper.ValueParse<double>(value);
}
/// <summary>
/// 将值转换为整数类型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int @int(object value)
{
return ConvertHelper.ValueParse<int>(value);
}
/// <summary>
/// 将值转换为长整型
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static int @long(object value)
{
return ConvertHelper.ValueParse<int>(value);
@@ -44,6 +92,12 @@ namespace Serein.Library
#endregion
/// <summary>
/// 获取集合或数组的长度
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static int len(object target)
{
// 获取数组或集合对象
@@ -70,28 +124,50 @@ namespace Serein.Library
}
}
/// <summary>
/// 将对象转换为字符串
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string str(object obj)
{
return obj?.ToString() ?? string.Empty;
}
/// <summary>
/// 获取全局数据
/// </summary>
/// <param name="name"></param>
/// <returns></returns>
public static object global(string name)
{
return SereinEnv.GetFlowGlobalData(name);
}
/// <summary>
/// 获取对象的类型
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
public static Type type(object type)
{
return type.GetType();
}
/// <summary>
/// 记录日志信息
/// </summary>
/// <param name="value"></param>
public static void log(object value)
{
SereinEnv.WriteLine(InfoType.INFO, value?.ToString());
}
/// <summary>
/// 等待一段时间
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static async Task sleep(object value)
{
if (value is int @int)

View File

@@ -2,7 +2,7 @@
<PropertyGroup>
<Title>SereinFow</Title>
<Version>1.2.2</Version>
<Version>1.2.3</Version>
<Description>动态节点流、可视化编辑的基本依赖支持导入C# DLL生成自定义节点提供二次开发支持适合用于可视化编程和流程设计</Description>
<PackageReadmeFile>README.md</PackageReadmeFile>
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
@@ -61,18 +61,13 @@
<Compile Remove="FlowNode\NodeModelBaseFunc.cs" />
<Compile Remove="FlowNode\ScriptFlowApi.cs" />
<Compile Remove="Utils\NativeDllHelper.cs" />
<Compile Remove="Utils\RemoteMsgUtil.cs" />
<Compile Remove="Utils\RemoteMsgUtil.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.ObjectPool" Version="9.0.0" />
<PackageReference Include="System.IO.Ports" Version="9.0.7" />
<PackageReference Include="System.Reactive" Version="6.0.1" />
<PackageReference Include="System.Threading.Channels" Version="8.0.0" />
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
<!--<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />-->
</ItemGroup>
<ItemGroup>
@@ -87,6 +82,8 @@
</ItemGroup>
<ItemGroup>
<!--<ProjectReference Include="..\Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj " OutputItemType="Analyzer" />-->
<!-- ReferenceOutputAssembly="false" -->
<ProjectReference Include="..\Serein.Library.MyGenerator\Serein.Library.NodeGenerator.csproj " OutputItemType="Analyzer" />
</ItemGroup>

View File

@@ -6,6 +6,9 @@ using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 数组操作的工具类
/// </summary>
public class ArrayHelper
{

View File

@@ -270,6 +270,14 @@ namespace Serein.Library.Utils
return (T)result;
}
/// <summary>
/// 将字符串转换为指定类型的值对象。
/// </summary>
/// <param name="valueStr"></param>
/// <param name="type"></param>
/// <returns></returns>
/// <exception cref="ArgumentException"></exception>
public static object ToValueData(this string valueStr, Type type)
{
if (string.IsNullOrWhiteSpace(valueStr))

View File

@@ -41,21 +41,40 @@ namespace Serein.Library.Utils
return null;
}
/// <summary>
/// 解析字典属性并创建对象实例
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static object Resolve(IDictionary<string, object> properties, string typeName)
{
var obj = CreateObjectWithProperties(properties, typeName);
//SetPropertyValues(obj, properties);
return obj;
}
/// <summary>
/// 尝试解析字典属性并创建对象实例
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <param name="result"></param>
/// <returns></returns>
public static bool TryResolve(IDictionary<string, object> properties, string typeName, out object result)
{
result = CreateObjectWithProperties(properties, typeName);
bool success = SetPropertyValuesWithValidation(result, properties);
return success;
// 打印赋值结果
}
// 递归方法:打印对象属性及类型
/// <summary>
/// 打印对象属性及类型
/// </summary>
/// <param name="obj"></param>
/// <param name="indent"></param>
public static void PrintObjectProperties(object obj, string indent = "")
{
var objType = obj.GetType();
@@ -327,7 +346,12 @@ namespace Serein.Library.Utils
}
#region
// 方法 1: 创建动态类型及其对象实例
/// <summary>
/// 创建动态类型及其对象实例
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static object CreateObjectWithProperties(IDictionary<string, object> properties, string typeName)
{
// 如果类型已经缓存,直接返回缓存的类型
@@ -410,8 +434,12 @@ namespace Serein.Library.Utils
return Activator.CreateInstance(dynamicType);
}
// 方法 2: 递归设置对象的属性值
public static void SetPropertyValues(object obj, Dictionary<string, object> properties)
/// <summary>
/// 递归设置对象的属性值
/// </summary>
/// <param name="obj"></param>
/// <param name="properties"></param>
private static void SetPropertyValues(object obj, Dictionary<string, object> properties)
{
var objType = obj.GetType();
@@ -439,8 +467,13 @@ namespace Serein.Library.Utils
}
}
}
// 方法 2: 递归设置对象的属性值(带验证)
/// <summary>
/// 递归设置对象的属性值(带验证)
/// </summary>
/// <param name="obj"></param>
/// <param name="properties"></param>
/// <returns></returns>
public static bool SetPropertyValuesWithValidation(object obj, IDictionary<string, object> properties)
{
var objType = obj.GetType();

View File

@@ -9,12 +9,15 @@ using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// Emit创建委托工具类
/// </summary>
public class EmitHelper
{
/// <summary>
/// 动态方法信息
/// </summary>
public class EmitMethodInfo
{
/// <summary>
@@ -39,6 +42,9 @@ namespace Serein.Library.Utils
}
/// <summary>
/// 方法类型枚举
/// </summary>
public enum EmitMethodType
{
/// <summary>
@@ -55,12 +61,19 @@ namespace Serein.Library.Utils
TaskHasResult,
}
public static bool IsGenericTask(Type returnType, out Type taskResult)
/// <summary>
/// 判断一个类型是否为泛型 Task&lt;T&gt; 或 Task并返回泛型参数类型如果有的话
/// </summary>
/// <param name="returnType"></param>
/// <param name="taskResult"></param>
/// <returns></returns>
#nullable enable
public static bool IsGenericTask(Type returnType, out Type? taskResult)
{
// 判断是否为 Task 类型或泛型 Task<T>
if (returnType == typeof(Task))
{
taskResult = null;
taskResult = typeof(void);
return true;
}
else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
@@ -78,7 +91,6 @@ namespace Serein.Library.Utils
}
}
/// <summary>
/// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托
/// </summary>
@@ -87,6 +99,10 @@ namespace Serein.Library.Utils
/// <returns></returns>
public static EmitMethodInfo CreateDynamicMethod(MethodInfo methodInfo,out Delegate @delegate)
{
if (methodInfo.DeclaringType == null)
{
throw new ArgumentNullException(nameof(methodInfo.DeclaringType));
}
EmitMethodInfo emitMethodInfo = new EmitMethodInfo();
bool IsTask = IsGenericTask(methodInfo.ReturnType, out var taskGenericsType);
bool IsTaskGenerics = taskGenericsType != null;
@@ -219,6 +235,10 @@ namespace Serein.Library.Utils
{
if (fieldInfo == null)
throw new ArgumentNullException(nameof(fieldInfo));
if (fieldInfo.DeclaringType == null)
throw new ArgumentNullException(nameof(fieldInfo.DeclaringType));
var method = new DynamicMethod(
fieldInfo.Name + "_Get",
@@ -257,6 +277,8 @@ namespace Serein.Library.Utils
{
if (fieldInfo == null)
throw new ArgumentNullException(nameof(fieldInfo));
if (fieldInfo.DeclaringType == null)
throw new ArgumentNullException(nameof(fieldInfo.DeclaringType));
if (fieldInfo.IsInitOnly)
throw new InvalidOperationException($"字段 {fieldInfo.Name} 是只读字段,无法设置值。");
@@ -299,6 +321,8 @@ namespace Serein.Library.Utils
{
if (propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
if (propertyInfo.DeclaringType == null)
throw new ArgumentNullException(nameof(propertyInfo.DeclaringType));
var getMethod = propertyInfo.GetGetMethod(true);
if (getMethod == null)
throw new InvalidOperationException($"属性 {propertyInfo.Name} 没有可用的 Getter。");
@@ -339,6 +363,9 @@ namespace Serein.Library.Utils
{
if (propertyInfo == null)
throw new ArgumentNullException(nameof(propertyInfo));
if (propertyInfo.DeclaringType == null)
throw new ArgumentNullException(nameof(propertyInfo.DeclaringType));
var setMethod = propertyInfo.GetSetMethod(true);
if (setMethod == null)
throw new InvalidOperationException($"属性 {propertyInfo.Name} 没有可用的 Setter。");

View File

@@ -59,6 +59,13 @@ namespace Serein.Library.Utils
return attribute != null ? (TResult)valueSelector(attribute) : default;
}
/// <summary>
/// 从枚举值的 BindValueAttribute 特性中 获取绑定的参数(用于绑定了某些内容的枚举值)
/// </summary>
/// <param name="enumType"></param>
/// <param name="enumValue"></param>
/// <param name="valueSelector"></param>
/// <returns></returns>
public static object GetBoundValue(Type enumType,object enumValue, Func<BindValueAttribute, object> valueSelector)
{
var fieldInfo = enumType.GetField(enumValue.ToString());

View File

@@ -146,7 +146,7 @@ namespace Serein.Library.Utils
var parameter = Expression.Parameter(typeof(object), "instance");
var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo);
if (IsGenericTask(methodInfo.ReturnType, out var taskResult))
if (EmitHelper.IsGenericTask(methodInfo.ReturnType, out var taskResult))
{
if (taskResult is null)
{
@@ -278,7 +278,7 @@ namespace Serein.Library.Utils
convertedArgs
);
if (IsGenericTask(methodInfo.ReturnType, out var taskResult))
if (EmitHelper.IsGenericTask(methodInfo.ReturnType, out var taskResult))
{
if (taskResult is null)
{
@@ -305,7 +305,7 @@ namespace Serein.Library.Utils
/// <summary>
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
/// 表达式树构建无参数,有返回值(Task&lt;object&gt;)的方法(触发器)
/// </summary>
public static Delegate MethodCallerAsync(Type type, MethodInfo methodInfo)
{
@@ -314,7 +314,7 @@ namespace Serein.Library.Utils
}
/// <summary>
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
/// 表达式树构建无参数,有返回值(Task&lt;object&gt;)的方法(触发器)
/// </summary>
/// <param name="type"></param>
/// <param name="methodInfo"></param>
@@ -332,7 +332,7 @@ namespace Serein.Library.Utils
/// <summary>
/// 表达式树构建多个参数,有返回值(Task-object)的方法(触发器)
/// 表达式树构建多个参数,有返回值(Task&lt;object&gt;)的方法(触发器)
/// </summary>
public static Delegate MethodCallerAsync(Type type, MethodInfo method, params Type[] parameterTypes)
{
@@ -342,7 +342,7 @@ namespace Serein.Library.Utils
}
/// <summary>
/// 表达式树构建多个参数,有返回值(Task<object>)的方法(触发器)
/// 表达式树构建多个参数,有返回值(Task&lt;object&gt;)的方法(触发器)
/// </summary>
private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo, Type[] parameterTypes)
{
@@ -384,31 +384,12 @@ namespace Serein.Library.Utils
public static bool IsGenericTask(Type returnType, out Type taskResult)
{
// 判断是否为 Task 类型或泛型 Task<T>
if (returnType == typeof(Task))
{
taskResult = null;
return true;
}
else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
// 获取泛型参数类型
Type genericArgument = returnType.GetGenericArguments()[0];
taskResult = genericArgument;
return true;
}
else
{
taskResult = null;
return false;
}
}
/// <summary>
/// / 自动创建一个委托,根据方法信息和类型
/// </summary>
/// <param name="type"></param>
/// <param name="methodInfo"></param>
/// <returns></returns>
public static Delegate AutoCreate(Type type, MethodInfo methodInfo)
{
Type returnType = methodInfo.ReturnType;

View File

@@ -9,6 +9,10 @@ using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 基于 Channel 的触发器实现
/// </summary>
/// <typeparam name="TSignal"></typeparam>
public class ChannelFlowTrigger<TSignal> : IFlowTrigger<TSignal>
{
// 使用并发字典管理每个枚举信号对应的 Channel
@@ -24,6 +28,13 @@ namespace Serein.Library.Utils
return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded<TriggerResult<object>>());
}
/// <summary>
/// 等待信号触发并指定超时时间
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="signal"></param>
/// <param name="outTime"></param>
/// <returns></returns>
public async Task<TriggerResult<TResult>> WaitTriggerWithTimeoutAsync<TResult>(TSignal signal, TimeSpan outTime)
{
var channel = GetOrCreateChannel(signal);
@@ -54,6 +65,12 @@ namespace Serein.Library.Utils
}
/// <summary>
/// 等待信号触发
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="signal"></param>
/// <returns></returns>
public async Task<TriggerResult<TResult>> WaitTriggerAsync<TResult>(TSignal signal)
{
var channel = GetOrCreateChannel(signal);
@@ -76,6 +93,13 @@ namespace Serein.Library.Utils
}
}
/// <summary>
/// 调用触发器
/// </summary>
/// <typeparam name="TResult"></typeparam>
/// <param name="signal"></param>
/// <param name="value"></param>
/// <returns></returns>
public async Task<bool> InvokeTriggerAsync<TResult>(TSignal signal, TResult value)
{
if (_channels.TryGetValue(signal, out var channel))
@@ -92,6 +116,9 @@ namespace Serein.Library.Utils
return false;
}
/// <summary>
/// 取消所有触发器
/// </summary>
public void CancelAllTrigger()
{
foreach (var channel in _channels.Values)

View File

@@ -141,13 +141,30 @@ namespace Serein.Library.Utils
{
private readonly Action<T> _onNext;
/// <summary>
/// 构造函数,接受一个 Action 作为回调
/// </summary>
/// <param name="onNext"></param>
public Observer(Action<T> onNext)
{
_onNext = onNext;
}
/// <summary>
/// 通知订阅者已完成或发生错误
/// </summary>
public void OnCompleted() { }
/// <summary>
/// 通知订阅者发生错误
/// </summary>
/// <param name="error"></param>
public void OnError(Exception error) { }
/// <summary>
/// 通知订阅者有新数据到来
/// </summary>
/// <param name="value"></param>
public void OnNext(T value)
{
_onNext?.Invoke(value);

View File

@@ -7,9 +7,20 @@ using System.Threading.Tasks;
namespace Serein.Library.Utils
{
/// <summary>
/// 触发器结果类,用于存储触发器的类型和返回值。
/// </summary>
/// <typeparam name="TResult"></typeparam>
public class TriggerResult<TResult>
{
/// <summary>
/// 触发类型
/// </summary>
public TriggerDescription Type { get; set; }
/// <summary>
/// 触发结果值
/// </summary>
public TResult Value { get; set; }
}
}

View File

@@ -23,28 +23,57 @@ namespace Serein.Library.Utils
JsonHelper.provider = jsonPortal;
}
/// <summary>
/// 反序列化Json文本为指定类型的对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jsonText"></param>
/// <returns></returns>
public static T Deserialize<T>(string jsonText)
{
return provider.Deserialize<T>(jsonText);
}
/// <summary>
/// 反序列化Json文本为指定类型的对象
/// </summary>
/// <param name="jsonText"></param>
/// <param name="type"></param>
/// <returns></returns>
public static object Deserialize(string jsonText, Type type)
{
return provider.Deserialize(jsonText, type);
}
/// <summary>
/// 解析Json文本为IJsonToken对象
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
public static IJsonToken Parse(string json)
{
return provider.Parse(json);
}
/// <summary>
/// 将对象序列化为Json文本
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static string Serialize(object obj)
{
return provider.Serialize(obj);
}
/// <summary>
/// 创建一个Json对象使用字典初始化
/// </summary>
/// <param name="init"></param>
/// <returns></returns>
public static IJsonToken Object(Action<Dictionary<string, object>> init)
{
var dict = new Dictionary<string, object>();
@@ -52,11 +81,21 @@ namespace Serein.Library.Utils
return provider.CreateObject(dict);
}
/// <summary>
/// 创建一个Json对象使用字典初始化
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public static IJsonToken Array(IEnumerable<object> values)
{
return provider.CreateArray(values);
}
/// <summary>
/// 将对象转换为JsonToken
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public static IJsonToken FromObject(object obj)
{
if (obj is System.Collections.IEnumerable && !(obj is string))

View File

@@ -32,6 +32,14 @@ namespace Serein.Library.Utils
}
}
/// <summary>
/// 异步选择器,返回一个新的集合,其中每个元素都是通过异步方法转换的结果。
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="source"></param>
/// <param name="method"></param>
/// <returns></returns>
public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, Task<TResult>> method)
{
@@ -39,6 +47,15 @@ namespace Serein.Library.Utils
}
/// <summary>
/// 异步选择器,返回一个新的集合,其中每个元素都是通过异步方法转换的结果。
/// </summary>
/// <typeparam name="TSource"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="source"></param>
/// <param name="method"></param>
/// <param name="concurrency"></param>
/// <returns></returns>
public static async Task<IEnumerable<TResult>> SelectAsync<TSource, TResult>(this IEnumerable<TSource> source,
Func<TSource, Task<TResult>> method,
int concurrency = int.MaxValue)

View File

@@ -28,23 +28,39 @@ namespace Serein.Library.Utils
internal T Value;
}
// 不使用System。Func{T},因为. net 2.0没有该类型。
/// <summary>
/// 不使用System。Func{T},因为. net 2.0没有该类型。
/// </summary>
/// <returns></returns>
public delegate T Factory();
// 池对象的存储。第一个项存储在专用字段中,因为我们希望能够满足来自它的大多数请求。
/// <summary>
/// 池对象的存储。第一个项存储在专用字段中,因为我们希望能够满足来自它的大多数请求。
/// </summary>
private T _firstItem;
private readonly Element[] _items;
// 工厂在池的生命周期内被存储。只有当池需要扩展时,我们才调用它。
// 与“new T”相比Func为实现者提供了更多的灵活性并且比“new T”更快
/// <summary>
/// 工厂在池的生命周期内被存储。只有当池需要扩展时,我们才调用它
/// 与“new T”相比Func为实现者提供了更多的灵活性并且比“new T”更快。
/// </summary>
private readonly Factory _factory;
/// <summary>
/// 创建一个新的对象池实例,使用指定的工厂函数和默认大小(处理器核心数的两倍)。
/// </summary>
/// <param name="factory"></param>
public ObjectPool(Factory factory)
: this(factory, Environment.ProcessorCount * 2)
{
}
/// <summary>
/// 创建一个新的对象池实例,使用指定的工厂函数和指定的大小。
/// </summary>
/// <param name="factory"></param>
/// <param name="size"></param>
public ObjectPool(Factory factory, int size)
{
Debug.Assert(size >= 1);
@@ -52,6 +68,10 @@ namespace Serein.Library.Utils
_items = new Element[size - 1];
}
/// <summary>
/// 创建一个新的实例。
/// </summary>
/// <returns></returns>
private T CreateInstance()
{
T inst = _factory();
@@ -82,6 +102,10 @@ namespace Serein.Library.Utils
return inst;
}
/// <summary>
/// 慢速分配方法,当第一个元素不可用时调用。
/// </summary>
/// <returns></returns>
private T AllocateSlow()
{
Element[] items = _items;

View File

@@ -669,6 +669,12 @@ namespace Serein.Library.Utils
#region
/// <summary>
/// 运行一个方法方法的参数类型由IOC容器提供
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
public ISereinIOC Run<T>(Action<T> action)
{
var service = Get<T>();
@@ -676,6 +682,13 @@ namespace Serein.Library.Utils
return this;
}
/// <summary>
/// 运行一个方法方法的参数类型由IOC容器提供
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
public ISereinIOC Run<T1, T2>(Action<T1, T2> action)
{
var service1 = Get<T1>();
@@ -685,6 +698,14 @@ namespace Serein.Library.Utils
return this;
}
/// <summary>
/// 运行一个方法方法的参数类型由IOC容器提供
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
public ISereinIOC Run<T1, T2, T3>(Action<T1, T2, T3> action)
{
var service1 = Get<T1>();
@@ -694,6 +715,15 @@ namespace Serein.Library.Utils
return this;
}
/// <summary>
/// 运行一个方法方法的参数类型由IOC容器提供
/// </summary>
/// <typeparam name="T1"></typeparam>
/// <typeparam name="T2"></typeparam>
/// <typeparam name="T3"></typeparam>
/// <typeparam name="T4"></typeparam>
/// <param name="action"></param>
/// <returns></returns>
public ISereinIOC Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
{
var service1 = Get<T1>();

View File

@@ -54,11 +54,12 @@ namespace Serein.NodeFlow.Env
private FlowWorkManagement flowWorkManagement;
private ISereinIOC externalIOC;
private Action<ISereinIOC> setDefultMemberOnReset;
private FlowWorkManagement? flowWorkManagement;
private ISereinIOC? externalIOC;
private Action<ISereinIOC>? setDefultMemberOnReset;
private bool IsUseExternalIOC = false;
private object lockObj = new object();
private readonly object lockObj = new object();
/// <summary>
/// 如果全局触发器还在运行,则为 Running 。
/// </summary>
@@ -313,7 +314,7 @@ namespace Serein.NodeFlow.Env
}*/
}
/// <inheritdoc/>
public void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC> setDefultMemberOnReset = null)
public void UseExternalIOC(ISereinIOC ioc, Action<ISereinIOC>? setDefultMemberOnReset = null)
{
IOC = ioc; // 设置IOC容器
this.setDefultMemberOnReset = setDefultMemberOnReset;

View File

@@ -4,12 +4,13 @@ using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using Serein.NodeFlow.Model.Operation;
using Serein.NodeFlow.Model.Operations;
using Serein.NodeFlow.Services;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Threading.Tasks;
using static Serein.Library.Api.IFlowEnvironment;
using IOperation = Serein.NodeFlow.Model.Operation.IOperation;
using IOperation = Serein.NodeFlow.Model.Operations.IOperation;
namespace Serein.NodeFlow.Env
{
@@ -47,7 +48,7 @@ namespace Serein.NodeFlow.Env
private readonly FlowLibraryService flowLibraryManagement;
private readonly FlowOperationService flowOperationService;
private readonly FlowModelService flowModelService;
private readonly NodeMVVMService nodeMVVMService;
//private readonly NodeMVVMService nodeMVVMService;
/// <summary>
@@ -74,17 +75,23 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 从Guid获取画布
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails)
/// <param name="nodeGuid">画布Guid</param>
/// <param name="canvasDetails">画布model</param>
/// <returns>是否获取成功</returns>
public bool TryGetCanvasModel(string nodeGuid, [NotNullWhen(true)] out FlowCanvasDetails? canvasDetails)
{
if (string.IsNullOrEmpty(nodeGuid))
{
canvasDetails = null;
canvasDetails = default;
return false;
}
return flowModelService.TryGetCanvasModel(nodeGuid, out canvasDetails);
if(flowModelService.TryGetCanvasModel(nodeGuid, out var flowCanvas))
{
canvasDetails = flowCanvas;
return true;
}
canvasDetails = default;
return false ;
}
@@ -92,9 +99,9 @@ namespace Serein.NodeFlow.Env
/// 从Guid获取节点
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
/// <param name="nodeModel">节点Model</param>
/// <returns>是否获取成功</returns>
public bool TryGetNodeModel(string nodeGuid, [NotNullWhen(true)]out IFlowNode? nodeModel)
{
if (string.IsNullOrEmpty(nodeGuid))
{
@@ -176,7 +183,7 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 创建节点
/// </summary>
/// <param name="nodeBase"></param>
/// <param name="nodeModel"></param>
private bool TryAddNode(IFlowNode nodeModel)
{
nodeModel.Guid ??= Guid.NewGuid().ToString();
@@ -218,7 +225,7 @@ namespace Serein.NodeFlow.Env
}
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void RemoveCanvas(string canvasGuid)
@@ -227,7 +234,7 @@ namespace Serein.NodeFlow.Env
{
CanvasGuid = canvasGuid
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void ConnectInvokeNode(string canvasGuid, string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionInvokeType invokeType)
@@ -244,7 +251,7 @@ namespace Serein.NodeFlow.Env
JunctionOfConnectionType = JunctionOfConnectionType.Invoke,
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void ConnectArgSourceNode(string canvasGuid, string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionArgSourceType argSourceType, int argIndex)
@@ -261,7 +268,7 @@ namespace Serein.NodeFlow.Env
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create,
JunctionOfConnectionType = JunctionOfConnectionType.Arg,
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
@@ -275,7 +282,7 @@ namespace Serein.NodeFlow.Env
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove,
JunctionOfConnectionType = JunctionOfConnectionType.Invoke,
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void RemoveArgSourceConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex)
@@ -289,10 +296,10 @@ namespace Serein.NodeFlow.Env
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Remove,
JunctionOfConnectionType = JunctionOfConnectionType.Arg,
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void CreateNode(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
public void CreateNode(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null)
{
IOperation operation = new CreateNodeOperation
{
@@ -301,7 +308,7 @@ namespace Serein.NodeFlow.Env
Position = position,
MethodDetailsInfo = methodDetailsInfo
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void RemoveNode(string canvasGuid, string nodeGuid)
@@ -311,7 +318,7 @@ namespace Serein.NodeFlow.Env
CanvasGuid = canvasGuid,
NodeGuid = nodeGuid
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void PlaceNodeToContainer(string canvasGuid, string nodeGuid, string containerNodeGuid)
@@ -322,7 +329,7 @@ namespace Serein.NodeFlow.Env
NodeGuid = nodeGuid,
ContainerNodeGuid = containerNodeGuid
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void TakeOutNodeToContainer(string canvasGuid, string nodeGuid)
@@ -332,36 +339,17 @@ namespace Serein.NodeFlow.Env
CanvasGuid = canvasGuid,
NodeGuid = nodeGuid,
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void SetStartNode(string canvasGuid, string nodeGuid)
{
IOperation operation = new SetStartNodeOperation
{
CanvasGuid = canvasGuid,
NewNodeGuid = nodeGuid,
};
_ = flowOperationService.Execute(operation);
return;
if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(nodeGuid, out var newStartNodeModel))
{
return;
}
var oldNodeGuid = canvasModel.StartNode?.Guid;
/*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel))
{
newStartNode.IsStart = false;
}*/
canvasModel.StartNode = newStartNodeModel;
//newStartNode.IsStart = true;
_ = SereinEnv.TriggerEvent(() =>
flowEnvironmentEvent.OnStartNodeChanged(
new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, newStartNodeModel.Guid)
));
return;
}
/// <inheritdoc/>
@@ -376,7 +364,7 @@ namespace Serein.NodeFlow.Env
ConnectionInvokeType = connectionType,
ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>
public void ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
@@ -387,7 +375,7 @@ namespace Serein.NodeFlow.Env
IsAdd = isAdd,
ParamIndex = paramIndex
};
flowOperationService.Execute(operation);
_ = flowOperationService.Execute(operation);
}
/// <inheritdoc/>

View File

@@ -170,7 +170,6 @@ namespace Serein.NodeFlow.Env
return (isConnect, remoteMsgUtil);
}*/
/// <inheritdoc/>
/* public async Task<bool> ExitFlowAsync()
{
return await currentFlowEnvironment.FlowControl.ExitFlowAsync();
@@ -190,9 +189,9 @@ namespace Serein.NodeFlow.Env
}*/
/// <inheritdoc/>
public async Task<SereinProjectData> GetProjectInfoAsync()
public SereinProjectData GetProjectInfoAsync()
{
return await currentFlowEnvironment.GetProjectInfoAsync();
return currentFlowEnvironment.GetProjectInfoAsync();
}
/// <inheritdoc/>

View File

@@ -3,112 +3,150 @@ using Serein.Library.Api;
namespace Serein.NodeFlow.Env
{
/// <summary>
/// 流程环境事件类
/// </summary>
public class FlowEnvironmentEvent : IFlowEnvironmentEvent
{
public event LoadDllHandler DllLoad;
public event ProjectLoadedHandler ProjectLoaded;
public event ProjectSavingHandler ProjectSaving;
public event NodeConnectChangeHandler NodeConnectChanged;
public event CanvasCreateHandler CanvasCreated;
public event CanvasRemoveHandler CanvasRemoved;
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;
/// <inheritdoc/>
public event LoadDllHandler? DllLoad;
/// <inheritdoc/>
public event ProjectLoadedHandler? ProjectLoaded;
/// <inheritdoc/>
public event ProjectSavingHandler? ProjectSaving;
/// <inheritdoc/>
public event NodeConnectChangeHandler? NodeConnectChanged;
/// <inheritdoc/>
public event CanvasCreateHandler? CanvasCreated;
/// <inheritdoc/>
public event CanvasRemoveHandler? CanvasRemoved;
/// <inheritdoc/>
public event NodeCreateHandler? NodeCreated;
/// <inheritdoc/>
public event NodeRemoveHandler? NodeRemoved;
/// <inheritdoc/>
public event NodePlaceHandler? NodePlace;
/// <inheritdoc/>
public event NodeTakeOutHandler? NodeTakeOut;
/// <inheritdoc/>
public event StartNodeChangeHandler? StartNodeChanged;
/// <inheritdoc/>
public event FlowRunCompleteHandler? FlowRunComplete;
/// <inheritdoc/>
public event MonitorObjectChangeHandler? MonitorObjectChanged;
/// <inheritdoc/>
public event NodeInterruptStateChangeHandler? NodeInterruptStateChanged;
/// <inheritdoc/>
public event ExpInterruptTriggerHandler? InterruptTriggered;
/// <inheritdoc/>
public event IOCMembersChangedHandler? IOCMembersChanged;
/// <inheritdoc/>
public event NodeLocatedHandler? NodeLocated;
/// <inheritdoc/>
public event EnvOutHandler? EnvOutput;
/// <inheritdoc/>
public void OnDllLoad(LoadDllEventArgs eventArgs)
{
DllLoad?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
{
ProjectLoaded?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnProjectSaving(ProjectSavingEventArgs eventArgs)
{
ProjectSaving?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs)
{
NodeConnectChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnCanvasCreated(CanvasCreateEventArgs eventArgs)
{
CanvasCreated?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs)
{
CanvasRemoved?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeCreated(NodeCreateEventArgs eventArgs)
{
NodeCreated?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeRemoved(NodeRemoveEventArgs eventArgs)
{
NodeRemoved?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodePlace(NodePlaceEventArgs eventArgs)
{
NodePlace?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
{
NodeTakeOut?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs)
{
StartNodeChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnFlowRunComplete(FlowEventArgs eventArgs)
{
FlowRunComplete?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs)
{
MonitorObjectChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs)
{
NodeInterruptStateChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs)
{
InterruptTriggered?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs)
{
IOCMembersChanged?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnNodeLocated(NodeLocatedEventArgs eventArgs)
{
NodeLocated?.Invoke(eventArgs);
}
/// <inheritdoc/>
public void OnEnvOutput(InfoType type, string value)
{
EnvOutput?.Invoke(type, value);

View File

@@ -5,6 +5,7 @@ using Serein.NodeFlow.Model.Nodes;
using Serein.NodeFlow.Services;
using Serein.NodeFlow.Tool;
using System;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace Serein.NodeFlow.Env
@@ -64,7 +65,7 @@ namespace Serein.NodeFlow.Env
/// 打开远程管理
/// </summary>
/// <param name="port"></param>
public async Task StartRemoteServerAsync(int port = 7525)
public void StartRemoteServerAsync(int port = 7525)
{
/*if (clientMsgManage is null)
{
@@ -116,7 +117,7 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// UI线程操作类
/// </summary>
public UIContextOperation UIContextOperation { get; private set; }
public UIContextOperation? UIContextOperation { get; private set; }
/// <summary>
/// 节点MVVM管理服务
@@ -173,15 +174,10 @@ namespace Serein.NodeFlow.Env
#region
/// <summary>
/// 装饰器运行环境类
/// </summary>
private readonly IFlowEnvironment mainFlowEnvironment;
/// <summary>
/// 流程运行时的IOC容器
/// </summary>
private ISereinIOC flowRunIOC;
private ISereinIOC? flowRunIOC;
/// <summary>
/// local环境的IOC容器主要用于注册本地环境的服务
@@ -203,28 +199,7 @@ namespace Serein.NodeFlow.Env
/// </summary>
private readonly FlowModelService _flowModelService;
/* /// <summary>
/// 环境加载的节点集合
/// Node Guid - Node Model
/// </summary>
private Dictionary<string, IFlowNode> NodeModels { get; } = [];
/// <summary>
/// 运行环境加载的画布集合
/// </summary>
private Dictionary<string, FlowCanvasDetails> FlowCanvass { get; } = [];
/// <summary>
/// 存放触发器节点(运行时全部调用)
/// </summary>
private List<SingleFlipflopNode> FlipflopNodes { get; } = [];
*/
/// <summary>
/// 流程任务管理
/// </summary>
private FlowWorkManagement? flowTaskManagement;
#endregion
@@ -247,34 +222,15 @@ namespace Serein.NodeFlow.Env
}
/// <summary>
/// 获取当前环境信息(远程连接)
/// </summary>
/// <returns></returns>
public async Task<FlowEnvInfo> GetEnvInfoAsync()
{
// 获取所有的程序集对应的方法信息(程序集相关的数据)
var libraryMdss = this._flowLibraryService.GetAllLibraryMds().ToArray();
// 获取当前项目的信息(节点相关的数据)
var project = await GetProjectInfoAsync(); // 远程连接获取远程环境项目信息
SereinEnv.WriteLine(InfoType.INFO, "已将当前环境信息发送到远程客户端");
return new FlowEnvInfo
{
Project = project, // 项目信息
LibraryMds = libraryMdss, // 环境方法
};
}
/// <summary>
/// 保存项目
/// </summary>
public void SaveProject()
{
var project = GetProjectInfoAsync();
Task.Run(async () =>
{
var project = await GetProjectInfoAsync();
await SereinEnv.TriggerEvent(() =>
{
Event.OnProjectSaving(new ProjectSavingEventArgs(project));
@@ -316,27 +272,19 @@ namespace Serein.NodeFlow.Env
_ = Task.Run(async () =>
{
// 加载画布
try
foreach (var canvasInfo in projectData.Canvass)
{
foreach (var canvasInfo in projectData.Canvass)
{
await LoadCanvasAsync(canvasInfo);
}
var nodeInfos = projectData.Nodes.ToList();
await FlowEdit.LoadNodeInfosAsync(nodeInfos); // 加载节点信息
// 加载画布
foreach (var canvasInfo in projectData.Canvass)
{
FlowEdit.SetStartNode(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点
}
Event.OnProjectLoaded(new ProjectLoadedEventArgs());
await LoadCanvasAsync(canvasInfo);
}
catch (Exception ex)
var nodeInfos = projectData.Nodes.ToList();
await FlowEdit.LoadNodeInfosAsync(nodeInfos); // 加载节点信息
// 加载画布
foreach (var canvasInfo in projectData.Canvass)
{
throw;
FlowEdit.SetStartNode(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点
}
Event.OnProjectLoaded(new ProjectLoadedEventArgs());
});
}
@@ -382,56 +330,11 @@ namespace Serein.NodeFlow.Env
Event.OnProjectLoaded(new ProjectLoadedEventArgs());
}
/// <summary>
/// 加载远程环境
/// </summary>
/// <param name="addres">远程环境地址</param>
/// <param name="port">远程环境端口</param>
/// <param name="token">密码</param>
/*public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
{
throw new NotImplementedException("远程环境未实现的方法 ConnectRemoteEnv");
*//*if (IsControlRemoteEnv)
{
await Console.Out.WriteLineAsync($"当前已经连接远程环境");
return (false, null);
}
// 没有连接远程环境,可以重新连接
var controlConfiguration = new RemoteMsgUtil.ControlConfiguration
{
Addres = addres,
Port = port,
Token = token,
*//*ThemeJsonKey = LocalFlowEnvironment.ThemeKey,
MsgIdJsonKey = LocalFlowEnvironment.MsgIdKey,
DataJsonKey = LocalFlowEnvironment.DataKey,*//*
};
var remoteMsgUtil = new RemoteMsgUtil(controlConfiguration);
var result = await remoteMsgUtil.ConnectAsync();
if (!result)
{
await Console.Out.WriteLineAsync("连接失败,请检查地址与端口是否正确");
return (false, null);
}
await Console.Out.WriteLineAsync("连接成功开始验证Token");
IsControlRemoteEnv = true;
return (true, remoteMsgUtil);*//*
}*/
/// <summary>
/// 退出远程环境
/// </summary>
public void ExitRemoteEnv()
{
IsControlRemoteEnv = false;
}
/// <summary>
/// 序列化当前项目的依赖信息、节点信息
/// </summary>
/// <returns></returns>
public async Task<SereinProjectData> GetProjectInfoAsync()
public SereinProjectData GetProjectInfoAsync()
{
var projectData = new SereinProjectData()
{
@@ -466,28 +369,6 @@ namespace Serein.NodeFlow.Env
}
}
/* /// <summary>
/// 加载本地程序集
/// </summary>
/// <param name="flowLibrary"></param>
public void LoadLibrary(FlowLibraryCache flowLibrary)
{
try
{
libraryInfo = FlowLibraryService.LoadFlowLibrary(flowLibrary);
if (mdInfos.Count > 0)
{
UIContextOperation?.Invoke(() => Event.OnDllLoad(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示
}
}
catch (Exception ex)
{
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载DLL文件{ex.Message}");
}
}*/
/// <summary>
/// 移除DLL
/// </summary>
@@ -572,9 +453,6 @@ namespace Serein.NodeFlow.Env
//}
}
private int _addCanvasCount = 0;
private async Task<FlowCanvasDetails> LoadCanvasAsync(FlowCanvasDetailsInfo info)
{
var model = new FlowCanvasDetails(this);
@@ -623,6 +501,7 @@ namespace Serein.NodeFlow.Env
/// <para>异步方法Func&lt;object,object[],Task&gt;</para>
/// <para>异步有返回值方法Func&lt;object,object[],Task&lt;object&gt;&gt;</para>
/// </summary>
/// <param name="assemblyName"></param>
/// <param name="methodName"></param>
/// <param name="delegateDetails"></param>
/// <returns></returns>
@@ -711,10 +590,11 @@ namespace Serein.NodeFlow.Env
/// <summary>
/// 从Guid获取画布
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <returns>节点Model</returns>
/// <param name="nodeGuid">画布Guid</param>
/// <param name="canvasDetails">画布实体</param>
/// <returns>是否获取成功</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails)
public bool TryGetCanvasModel(string nodeGuid,[NotNullWhen(true)] out FlowCanvasDetails? canvasDetails)
{
if (string.IsNullOrEmpty(nodeGuid))
{
@@ -729,9 +609,10 @@ namespace Serein.NodeFlow.Env
/// 从Guid获取节点
/// </summary>
/// <param name="nodeGuid">节点Guid</param>
/// <param name="nodeModel">节点Guid</param>
/// <returns>节点Model</returns>
/// <exception cref="ArgumentNullException">无法获取节点、Guid/节点为null时报错</exception>
public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel)
public bool TryGetNodeModel(string nodeGuid, [NotNullWhen(true)] out IFlowNode? nodeModel)
{
if (string.IsNullOrEmpty(nodeGuid))
{

View File

@@ -158,10 +158,11 @@ namespace Serein.NodeFlow
/// <param name="sb">字符串构建器</param>
/// <param name="retractCount">缩进次数4个空格</param>
/// <param name="code">要添加的代码</param>
/// <param name="isWrapping">是否换行</param>
/// <returns>字符串构建器本身</returns>
public static StringBuilder AppendCode(this StringBuilder sb,
int retractCount = 0,
string code = null,
string? code = null,
bool isWrapping = true)
{
if (!string.IsNullOrWhiteSpace(code))

View File

@@ -4,22 +4,25 @@ using Serein.Library.Api;
namespace Serein.NodeFlow
{
/// <summary>
/// 流程任务类,包含流程的起始节点和所有节点信息。
/// </summary>
public class FlowTask
{
/// <summary>
/// 是否异步启动流程
/// </summary>
public bool IsTaskAsync { get; set; }
public bool IsTaskAsync { get; set; }
/// <summary>
/// 流程起始节点
/// </summary>
public Func<IFlowNode> GetStartNode { get; set; }
public Func<IFlowNode>? GetStartNode { get; set; }
/// <summary>
/// 获取当前画布流程的所有节点
/// </summary>
public Func<List<IFlowNode>> GetNodes { get; set; }
public Func<List<IFlowNode>>? GetNodes { get; set; }
}
/// <summary>
@@ -30,11 +33,11 @@ namespace Serein.NodeFlow
/// <summary>
/// 流程IOC容器
/// </summary>
public ISereinIOC FlowIOC { get; set; }
public required ISereinIOC FlowIOC { get; set; }
/// <summary>
/// 流程运行环境
/// </summary>
public IFlowEnvironment Environment { get; set; }// = environment;
public required IFlowEnvironment Environment { get; set; }// = environment;
/// <summary>
/// 表示运行环境状态
@@ -44,35 +47,30 @@ namespace Serein.NodeFlow
/// <summary>
/// 上下文线程池
/// </summary>
public Serein.Library.Utils.ObjectPool<IFlowContext> FlowContextPool { get; set; }
public required Serein.Library.Utils.ObjectPool<IFlowContext> FlowContextPool { get; set; }
/// <summary>
/// 每个画布需要启用的节点
/// </summary>
public Dictionary<string, FlowTask> Flows { get; set; }
/// <summary>
/// 当前任务加载的所有节点
/// </summary>
//public List<NodeModelBase> Nodes { get; set; }// = nodes;
public Dictionary<string, FlowTask> Flows { get; set; } = [];
/// <summary>
/// 需要注册的类型
/// </summary>
public Dictionary<RegisterSequence, List<Type>> AutoRegisterTypes { get; set; } //= autoRegisterTypes;
public Dictionary<RegisterSequence, List<Type>> AutoRegisterTypes { get; set; } = [];
/// <summary>
/// 初始化时需要的方法
/// </summary>
public List<MethodDetails> InitMds { get; set; }// = initMds;
public List<MethodDetails> InitMds { get; set; } = [];
/// <summary>
/// 加载时需要的方法
/// </summary>
public List<MethodDetails> LoadMds { get; set; }// = loadMds;
public List<MethodDetails> LoadMds { get; set; } = [];
/// <summary>
/// 退出时需要调用的方法
/// </summary>
public List<MethodDetails> ExitMds { get; set; } //= exitMds;
public List<MethodDetails> ExitMds { get; set; } = [];
}
}

View File

@@ -143,7 +143,7 @@ namespace Serein.NodeFlow.Model.Library
// 从 scanTypes.Type 创建的方法信息
// Md : 方法描述
// Dd 方法对应的Emit委托
List<LibraryMdDd> detailss = new List<LibraryMdDd>();
List<LibraryMthodInfo> detailss = new List<LibraryMthodInfo>();
// 遍历扫描的类型
foreach ((var type, var flowName) in scanTypes)
@@ -159,7 +159,7 @@ namespace Serein.NodeFlow.Model.Library
continue;
}
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
detailss.Add(new LibraryMdDd(mi, md, dd));
detailss.Add(new LibraryMthodInfo(mi, md, dd));
}
}

View File

@@ -1,20 +0,0 @@
using Serein.Library;
using System.Reflection;
namespace Serein.NodeFlow.Model.Library
{
public class LibraryMdDd
{
public MethodDetails MethodDetails { get; }
public MethodInfo MethodInfo { get; }
public DelegateDetails DelegateDetails { get; }
public LibraryMdDd(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
{
MethodDetails = methodDetails;
MethodInfo = methodInfo;
DelegateDetails = delegateDetails;
}
}
}

View File

@@ -0,0 +1,39 @@
using Serein.Library;
using System.Reflection;
namespace Serein.NodeFlow.Model.Library
{
/// <summary>
/// 库方法信息类,包含方法的详细信息、反射信息和委托信息。
/// </summary>
public class LibraryMthodInfo
{
/// <summary>
/// 方法的详细信息。
/// </summary>
public MethodDetails MethodDetails { get; }
/// <summary>
/// 方法的反射信息,包含方法名称、参数类型等。
/// </summary>
public MethodInfo MethodInfo { get; }
/// <summary>
/// Emit构造委托
/// </summary>
public DelegateDetails DelegateDetails { get; }
/// <summary>
/// 构造一个新的库方法信息实例。
/// </summary>
/// <param name="methodInfo"></param>
/// <param name="methodDetails"></param>
/// <param name="delegateDetails"></param>
public LibraryMthodInfo(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
{
MethodDetails = methodDetails;
MethodInfo = methodInfo;
DelegateDetails = delegateDetails;
}
}
}

View File

@@ -140,7 +140,6 @@ namespace Serein.NodeFlow.Model.Nodes
/// 从节点信息加载节点
/// </summary>
/// <param name="nodeModel"></param>
/// <param name="canvas"></param>
/// <param name="nodeInfo"></param>
/// <returns></returns>
public static void LoadInfo(this IFlowNode nodeModel, NodeInfo nodeInfo)
@@ -243,97 +242,6 @@ namespace Serein.NodeFlow.Model.Nodes
}
}
/// <summary>
/// 检查监视表达式是否生效
/// </summary>
/// <param name="nodeModel">节点Moel</param>
/// <param name="context">上下文</param>
/// <param name="newData">新的数据</param>
/// <returns></returns>
/*public static async Task CheckExpInterrupt(this NodeModelBase nodeModel, IDynamicContext context, object newData = null)
{
string guid = nodeModel.Guid;
context.AddOrUpdate(guid, newData); // 上下文中更新数据
if (newData is null)
{
}
else
{
await nodeModel.MonitorObjExpInterrupt(context, newData, 0); // 首先监视对象
await nodeModel.MonitorObjExpInterrupt(context, newData, 1); // 然后监视节点
//nodeModel.FlowData = newData; // 替换数据
}
}*/
/// <summary>
/// 监视对象表达式中断
/// </summary>
/// <param name="nodeModel"></param>
/// <param name="context"></param>
/// <param name="data"></param>
/// <param name="monitorType"></param>
/// <returns></returns>
/*private static async Task MonitorObjExpInterrupt(this NodeModelBase nodeModel, IDynamicContext context, object data, int monitorType)
{
MonitorObjectEventArgs.ObjSourceType sourceType;
string key;
if (monitorType == 0)
{
key = data?.GetType()?.FullName;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
}
else
{
key = nodeModel.Guid;
sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj;
}
if (string.IsNullOrEmpty(key))
{
return;
}
//(var isMonitor, var exps) = await context.Env.CheckObjMonitorStateAsync(key);
//if (isMonitor) // 如果新的数据处于查看状态通知UI进行更新交给运行环境判断
//{
// context.Env.MonitorObjectNotification(nodeModel.Guid, data, sourceType); // 对象处于监视状态通知UI更新数据显示
// if (exps.Length > 0)
// {
// // 表达式环境下判断是否需要执行中断
// bool isExpInterrupt = false;
// string exp = "";
// // 判断执行监视表达式,直到为 true 时退出
// for (int i = 0; i < exps.Length && !isExpInterrupt; i++)
// {
// exp = exps[i];
// if (string.IsNullOrEmpty(exp)) continue;
// // isExpInterrupt = SereinConditionParser.To(data, exp);
// }
// if (isExpInterrupt) // 触发中断
// {
// nodeModel.DebugSetting.IsInterrupt = true;
// if (await context.Env.SetNodeInterruptAsync(nodeModel.Guid,true))
// {
// context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp);
// var cancelType = await nodeModel.DebugSetting.GetInterruptTask();
// await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支");
// nodeModel.DebugSetting.IsInterrupt = false;
// }
// }
// }
//}
}*/
/// <summary>
/// 不再中断
/// </summary>
public static void CancelInterrupt(IFlowNode nodeModel)
{
nodeModel.DebugSetting.IsInterrupt = false;
nodeModel.DebugSetting.CancelInterrupt?.Invoke();
}
#if DEBUG
/// <summary>
/// 程序集更新,更新节点方法描述、以及所有入参描述的类型

View File

@@ -13,49 +13,49 @@ namespace Serein.NodeFlow.Model.Nodes
/// <summary>
/// 节点基类(数据)
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node)]
public abstract partial class NodeModelBase : IFlowNode
{
/// <summary>
/// 节点运行环境
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private IFlowEnvironment _env;
/// <summary>
/// 标识节点对象全局唯一
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private string _guid;
/// <summary>
/// 描述节点对应的控件类型
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private NodeControlType _controlType;
/// <summary>
/// 所属画布
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private FlowCanvasDetails _canvasDetails ;
/// <summary>
/// 在画布中的位置
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private PositionOfUI _position ;
/// <summary>
/// 显示名称
/// </summary>
[PropertyInfo]
[DataInfo]
private string _displayName;
/// <summary>
/// 是否公开
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private bool _isPublic;
/* /// <summary>
@@ -67,13 +67,13 @@ namespace Serein.NodeFlow.Model.Nodes
/// <summary>
/// 附加的调试功能
/// </summary>
[PropertyInfo(IsProtection = true)]
[DataInfo(IsProtection = true)]
private NodeDebugSetting _debugSetting ;
/// <summary>
/// 方法描述。包含参数信息。不包含Method与委托如若需要调用对应的方法需要通过MethodName从环境中获取委托进行调用。
/// </summary>
[PropertyInfo]
[DataInfo]
private MethodDetails _methodDetails ;
}
@@ -90,6 +90,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
public virtual int MaxChildrenCount { get; } = 0;
/// <summary>
/// 创建一个节点模型的基类实例
/// </summary>
/// <param name="environment"></param>
public NodeModelBase(IFlowEnvironment environment)
{
PreviousNodes = new Dictionary<ConnectionInvokeType, List<IFlowNode>>();

View File

@@ -47,7 +47,6 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
/// <param name="context">流程上下文</param>
/// <param name="token"></param>
/// <param name="args">自定义参数</param>
/// <returns>节点传回数据对象</returns>
public virtual async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
{

View File

@@ -9,6 +9,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
public class SingleActionNode : NodeModelBase
{
/// <summary>
/// 构造一个新的单动作节点实例。
/// </summary>
/// <param name="environment"></param>
public SingleActionNode(IFlowEnvironment environment):base(environment)
{

View File

@@ -9,25 +9,25 @@ namespace Serein.NodeFlow.Model.Nodes
/// <summary>
/// 条件节点(用于条件控件)
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
public partial class SingleConditionNode : NodeModelBase
{
/// <summary>
/// 是否为自定义参数
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private bool _isExplicitData;
/// <summary>
/// 自定义参数值
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string? _explicitData;
/// <summary>
/// 条件表达式
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string _expression;
}
@@ -44,7 +44,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
private const int INDEX_EXPRESSION = 0;
/// <summary>
/// 条件节点构造函数
/// </summary>
/// <param name="environment"></param>
public SingleConditionNode(IFlowEnvironment environment):base(environment)
{
this.IsExplicitData = false;
@@ -52,6 +55,9 @@ namespace Serein.NodeFlow.Model.Nodes
this.Expression = "PASS";
}
/// <summary>
/// 创建节点时调用的方法
/// </summary>
public override void OnCreating()
{
// 这里的这个参数是为了方便使用入参控制点,参数无意义
@@ -106,6 +112,7 @@ namespace Serein.NodeFlow.Model.Nodes
/// 重写节点的方法执行
/// </summary>
/// <param name="context"></param>
/// <param name="token"></param>
/// <returns></returns>
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
{

View File

@@ -8,19 +8,21 @@ namespace Serein.NodeFlow.Model.Nodes
/// <summary>
/// Expression Operation - 表达式操作
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
public partial class SingleExpOpNode : NodeModelBase
{
/// <summary>
/// 表达式
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string _expression;
}
/// <summary>
/// 表达式节点模型基类
/// </summary>
public partial class SingleExpOpNode : NodeModelBase
{
/// <summary>
@@ -33,6 +35,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
private const int INDEX_EXPRESSION = 0;
/// <summary>
/// 表达式节点构造函数
/// </summary>
/// <param name="environment"></param>
public SingleExpOpNode(IFlowEnvironment environment) : base(environment)
{
@@ -87,7 +93,12 @@ namespace Serein.NodeFlow.Model.Nodes
this.Expression = nodeInfo.CustomData?.Expression ?? "";
}
/// <summary>
/// 执行节点操作
/// </summary>
/// <param name="context"></param>
/// <param name="token"></param>
/// <returns></returns>
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
{
if(token.IsCancellationRequested) return FlowResult.Fail(this.Guid, context, "流程已通过token取消");

View File

@@ -10,7 +10,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
public class SingleFlipflopNode : NodeModelBase
{
/// <summary>
/// 构造一个新的单触发器节点实例。
/// </summary>
/// <param name="environment"></param>
public SingleFlipflopNode(IFlowEnvironment environment) : base(environment)
{
@@ -21,6 +24,7 @@ namespace Serein.NodeFlow.Model.Nodes
/// 执行触发器进行等待触发
/// </summary>
/// <param name="context"></param>
/// <param name="token"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)

View File

@@ -7,26 +7,26 @@ using System.Reflection;
namespace Serein.NodeFlow.Model.Nodes
{
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
public partial class SingleFlowCallNode
{
/// <summary>
/// 目标公开节点
/// </summary>
[PropertyInfo(IsNotification = true)]
private string targetNodeGuid;
[DataInfo(IsNotification = true)]
private string targetNodeGuid = string.Empty;
/// <summary>
/// 使用目标节点的参数如果为true则使用目标节点的入参如果为false则使用节点自定义入参
/// </summary>
[PropertyInfo(IsNotification = true)]
private bool _isShareParam ;
[DataInfo(IsNotification = true)]
private bool _isShareParam ;
/// <summary>
/// 接口全局名称
/// </summary>
[PropertyInfo(IsNotification = true)]
private string _apiGlobalName;
[DataInfo(IsNotification = true)]
private string _apiGlobalName = string.Empty;
}
@@ -45,6 +45,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
public MethodDetails CacheMethodDetails { get; private set; }
/// <summary>
/// 流程接口节点
/// </summary>
/// <param name="environment"></param>
public SingleFlowCallNode(IFlowEnvironment environment) : base(environment)
{
@@ -207,7 +211,7 @@ namespace Serein.NodeFlow.Model.Nodes
}
private static Dictionary<string, int> ApiInvokeNameCache = new Dictionary<string, int>();
public static int getApiInvokeNameCount = 0;
private static int getApiInvokeNameCount = 0;
private static string GetApiInvokeName(SingleFlowCallNode node, string apiName)
{
if (ApiInvokeNameCache.ContainsKey(apiName))
@@ -223,6 +227,12 @@ namespace Serein.NodeFlow.Model.Nodes
return $"{apiName}";
}
}
/// <summary>
/// 获取流程接口节点的名称
/// </summary>
/// <param name="node"></param>
/// <returns></returns>
public static string GetApiInvokeName(SingleFlowCallNode node)
{
if(node.TargetNode is null)

View File

@@ -9,13 +9,13 @@ namespace Serein.NodeFlow.Model.Nodes
/// <summary>
/// Expression Operation - 表达式操作
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
public partial class SingleGlobalDataNode : NodeModelBase
{
/// <summary>
/// 全局数据的Key名称
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string _keyName;
}
@@ -36,6 +36,10 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
public override int MaxChildrenCount => 1;
/// <summary>
/// 全局数据节点,允许放置一个数据源节点,通常是[Action]或[Script]节点,用于获取全局数据。
/// </summary>
/// <param name="environment"></param>
public SingleGlobalDataNode(IFlowEnvironment environment) : base(environment)
{
}
@@ -85,7 +89,11 @@ namespace Serein.NodeFlow.Model.Nodes
}
/// <summary>
/// 从容器中取出节点
/// </summary>
/// <param name="nodeModel"></param>
/// <returns></returns>
public bool TakeOutNode(IFlowNode nodeModel)
{
if (ChildrenNode.Contains(nodeModel))
@@ -102,7 +110,10 @@ namespace Serein.NodeFlow.Model.Nodes
}
public async void TakeOutAll()
/// <summary>
/// 从容器中取出所有节点
/// </summary>
public void TakeOutAll()
{
foreach (var nodeModel in ChildrenNode)
{
@@ -111,12 +122,13 @@ namespace Serein.NodeFlow.Model.Nodes
DataNode = null;
}
/// <summary>
/// 设置全局数据
/// </summary>
/// <param name="context"></param>
/// <param name="token"></param>
/// <returns></returns>
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
{

View File

@@ -10,27 +10,30 @@ using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Nodes
{
/// <summary>
/// 单脚本节点(用于脚本控件)
/// </summary>
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
public partial class SingleNetScriptNode : NodeModelBase
{
/// <summary>
/// 脚本代码
/// </summary>
[PropertyInfo(IsNotification = true)]
private string _script;
[DataInfo(IsNotification = true)]
private string _script = string.Empty;
/// <summary>
/// 功能提示
/// </summary>
[PropertyInfo(IsNotification = true)]
[DataInfo(IsNotification = true)]
private string _tips = "写一下提示吧";
/// <summary>
/// 依赖路径
/// </summary>
[PropertyInfo(IsNotification = true)]
private List<string> _libraryFilePaths;
[DataInfo(IsNotification = true)]
private List<string> _libraryFilePaths = [];
}
@@ -41,46 +44,16 @@ namespace Serein.NodeFlow.Model.Nodes
/// </summary>
public override bool IsBase => true;
/// <summary>
/// 脚本代码
/// </summary>
/// <param name="environment"></param>
public SingleNetScriptNode(IFlowEnvironment environment) : base(environment)
{
this.Env = environment;
}
public override void OnCreating()
{
//MethodInfo? method = this.GetType().GetMethod(nameof(GetFlowApi));
//if (method != null)
//{
// ScriptInterpreter.AddFunction(nameof(GetFlowApi), method, () => this); // 挂载获取流程接口
//}
//var md = MethodDetails;
//var pd = md.ParameterDetailss ??= new ParameterDetails[1];
//md.ParamsArgIndex = 0;
//pd[0] = new ParameterDetails
//{
// Index = 0,
// Name = "object",
// IsExplicitData = true,
// DataValue = string.Empty,
// DataType = typeof(object),
// ExplicitType = typeof(object),
// ArgDataSourceNodeGuid = string.Empty,
// ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
// NodeModel = this,
// InputType = ParameterValueInputType.Input,
// Items = null,
// IsParams = true,
// Description = "脚本节点入参"
//};
}
/// <summary>
/// 导出脚本代码
/// </summary>

View File

@@ -18,11 +18,11 @@ using System.Xml.Linq;
namespace Serein.NodeFlow.Model.Nodes
{
[NodeProperty(ValuePath = NodeValuePath.Node)]
[FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)]
public partial class SingleScriptNode : NodeModelBase
{
[PropertyInfo(IsNotification = true)]
private string _script;
[DataInfo(IsNotification = true)]
private string _script = string.Empty;
}
/// <summary>
@@ -293,6 +293,7 @@ namespace Serein.NodeFlow.Model.Nodes
/// 执行脚本
/// </summary>
/// <param name="context"></param>
/// <param name="token"></param>
/// <returns></returns>
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
{

View File

@@ -8,13 +8,30 @@ using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Nodes
{
/// <summary>
/// 单个UI节点适用于需要在流程中嵌入用户自定义控件的场景。
/// </summary>
public class SingleUINode : NodeModelBase
{
public IEmbeddedContent Adapter { get; private set; }
/// <summary>
/// 适配的UI控件必须实现IEmbeddedContent接口。
/// </summary>
public IEmbeddedContent? Adapter { get; private set; }
/// <summary>
/// 单个UI节点构造函数初始化流程环境。
/// </summary>
/// <param name="environment"></param>
public SingleUINode(IFlowEnvironment environment) : base(environment)
{
}
/// <summary>
/// 执行节点逻辑适用于嵌入式UI控件的流程节点。
/// </summary>
/// <param name="context"></param>
/// <param name="token"></param>
/// <returns></returns>
public override async Task<FlowResult> ExecutingAsync(IFlowContext context, CancellationToken token)
{
if (token.IsCancellationRequested) return FlowResult.Fail(this.Guid, context, "流程已通过token取消");

View File

@@ -11,7 +11,7 @@ using System.Text;
using System.Threading.Tasks;
using static Serein.Library.Api.NodeConnectChangeEventArgs;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
/// <summary>
/// 节点连接状态发生改变
@@ -45,6 +45,7 @@ namespace Serein.NodeFlow.Model.Operation
/// </summary>
public JunctionType ToNodeJunctionType { get; set; }
/// <summary>
/// 连接类型
/// </summary>
public ConnectionInvokeType ConnectionInvokeType { get; set; }
@@ -69,9 +70,9 @@ namespace Serein.NodeFlow.Model.Operation
public override bool IsCanUndo => false;
#region
private FlowCanvasDetails FlowCanvas;
private IFlowNode FromNode;
private IFlowNode ToNode;
private FlowCanvasDetails? FlowCanvas;
private IFlowNode? FromNode;
private IFlowNode? ToNode;
#endregion
public override bool ValidationParameter()
@@ -155,7 +156,10 @@ namespace Serein.NodeFlow.Model.Operation
/// </summary>
private async Task<bool> CreateInvokeConnection()
{
IFlowNode fromNode = FromNode ;
ArgumentNullException.ThrowIfNull(FlowCanvas);
ArgumentNullException.ThrowIfNull(FromNode);
ArgumentNullException.ThrowIfNull(ToNode);
IFlowNode fromNode = FromNode;
IFlowNode toNode = ToNode;
ConnectionInvokeType invokeType = ConnectionInvokeType;
if (fromNode.ControlType == NodeControlType.FlowCall)
@@ -340,6 +344,10 @@ namespace Serein.NodeFlow.Model.Operation
/// </summary>
private async Task<bool> RemoveInvokeConnection()
{
ArgumentNullException.ThrowIfNull(FlowCanvas);
ArgumentNullException.ThrowIfNull(FromNode);
ArgumentNullException.ThrowIfNull(ToNode);
FromNode.SuccessorNodes[ConnectionInvokeType].Remove(ToNode);
ToNode.PreviousNodes[ConnectionInvokeType].Remove(FromNode);
@@ -383,7 +391,12 @@ namespace Serein.NodeFlow.Model.Operation
/// </summary>
/// <exception cref="Exception"></exception>
private async Task<bool> CreateArgConnection()
{/*
{
ArgumentNullException.ThrowIfNull(FlowCanvas);
ArgumentNullException.ThrowIfNull(FromNode);
ArgumentNullException.ThrowIfNull(ToNode);
/*
IFlowNode fromNodeControl = ToNode;
IFlowNode toNodeControl = ToNode;*/
ConnectionArgSourceType type = ConnectionArgSourceType;
@@ -520,11 +533,12 @@ namespace Serein.NodeFlow.Model.Operation
/// <summary>
/// 移除参数连接关系
/// </summary>
/// <param name="fromNodeControl"></param>
/// <param name="toNodeControl"></param>
/// <param name="index"></param>
private async Task<bool> RemoveArgConnection()
{
ArgumentNullException.ThrowIfNull(FlowCanvas);
ArgumentNullException.ThrowIfNull(FromNode);
ArgumentNullException.ThrowIfNull(ToNode);
if (ToNode.MethodDetails.ParameterDetailss is null) return false;
var type = ToNode.MethodDetails.ParameterDetailss[ArgIndex].ArgDataSourceType;
FromNode.NeedResultNodes[type].Remove(ToNode);

View File

@@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
internal class ChangeParameterOperation : OperationBase
{

View File

@@ -9,7 +9,7 @@ using System.Net.Mime;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
/// <summary>
/// 放置节点操作

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
/// <summary>

View File

@@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
internal class CreateCanvasOperation : OperationBase
{

View File

@@ -9,7 +9,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
internal class CreateNodeOperation : OperationBase
{

View File

@@ -9,7 +9,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
internal interface IOperation
{
@@ -64,6 +64,7 @@ namespace Serein.NodeFlow.Model.Operation
[AutoInjection]
protected IFlowEnvironmentEvent flowEnvironmentEvent;
public abstract string Theme { get;}
/// <summary>

View File

@@ -7,7 +7,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
internal class RemoveCanvasOperation : OperationBase
{

View File

@@ -2,7 +2,7 @@
using Serein.Library.Api;
using System.Reflection.Metadata;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
internal class RemoveNodeOperation : OperationBase
{
@@ -15,6 +15,7 @@ namespace Serein.NodeFlow.Model.Operation
/// 节点所在画布
/// </summary>
private FlowCanvasDetails flowCanvasDetails;
/// <summary>
/// 被删除的节点
/// </summary>
@@ -46,9 +47,19 @@ namespace Serein.NodeFlow.Model.Operation
{
if (!ValidationParameter()) return false;
// 需要移除对应的方法调用、以及参数获取调用
// 需要移除对应的方法调用、参数获取调用以及子节点信息
// 还需要记录移除的事件参数,用以撤销恢复
if (flowNode.ChildrenNode.Count > 0)
{
// 如果该节点存在子节点,则删除所有子节点
foreach(var child in flowNode.ChildrenNode)
{
flowEnvironment.FlowEdit.RemoveNode(CanvasGuid, child.Guid);
}
}
#region
// 检查该节点的前继节点,然后从这些前继节点中移除与该节点的连接关系

View File

@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
/// <summary>

View File

@@ -6,7 +6,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.NodeFlow.Model.Operation
namespace Serein.NodeFlow.Model.Operations
{
/// <summary>
/// 设置起始节点

View File

@@ -42,22 +42,11 @@ namespace Serein.NodeFlow
throw new NotImplementedException();
}
//public object? GetArgData(IDynamicContext context, int index)
//{
// var _paramsKey = $"{context?.Guid}_{NodeModel.Guid}_Params";
// var obj = context?.GetFlowData(_paramsKey);
// if (obj is object[] @params && index < @params.Length)
// {
// return @params[index];
// }
// return null;
//}
//public object? GetFlowData(IDynamicContext context)
//{
// return context?.GetFlowData(NodeModel.Guid);
//}
/// <summary>
/// 获取全局数据
/// </summary>
/// <param name="keyName"></param>
/// <returns></returns>
public object? GetGlobalData(string keyName)
{
return SereinEnv.GetFlowGlobalData(keyName);

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Version>1.2.2</Version>
<Version>1.2.3</Version>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
@@ -54,6 +54,7 @@
<Compile Remove="Tool\ExpressionHelper.cs" />
<Compile Remove="Tool\FlowLibraryAssemblyContext2.cs" />
<Compile Remove="Tool\NodeModelBaseFunc.cs" />
<Compile Remove="Tool\ObjDynamicCreateHelper.cs" />
<Compile Remove="Tool\TcsSignal.cs" />
<Compile Remove="Tool\ToCSharpCodeHelper.cs" />
</ItemGroup>

View File

@@ -77,6 +77,7 @@ namespace Serein.NodeFlow.Services
/// 生成完全的xml注释
/// </summary>
/// <param name="context"></param>
/// <param name="retractCount"></param>
/// <returns></returns>
public static string ToXmlComments(this string context, int retractCount = 0)
{

View File

@@ -11,7 +11,7 @@ namespace Serein.NodeFlow.Services
/// <summary>
/// 流程API服务用于外部调用流程接口
/// </summary>
public class FlowApiService
internal class FlowApiService
{
private readonly IFlowEnvironment flowEnvironment;
private readonly FlowModelService flowModelService;

View File

@@ -20,6 +20,11 @@ namespace Serein.NodeFlow.Services
private readonly FlowModelService flowModelService;
private readonly FlowLibraryService flowLibraryService;
/// <summary>
/// 流程代码生成服务
/// </summary>
/// <param name="flowModelService"></param>
/// <param name="flowLibraryService"></param>
public FlowCoreGenerateService(FlowModelService flowModelService ,FlowLibraryService flowLibraryService )
{
this.flowModelService = flowModelService;
@@ -429,7 +434,6 @@ namespace Serein.NodeFlow.Services
if (param is null) return;
if (pds is null) return;
bool isGetPreviousNode = false;
for (int index = 0; index < pds.Length; index++)
{
ParameterDetails? pd = pds[index];
@@ -798,7 +802,6 @@ namespace Serein.NodeFlow.Services
if (param is null) return;
if (pds is null) return;
bool isGetPreviousNode = false;
for (int index = 0; index < pds.Length; index++)
{
ParameterDetails? pd = pds[index];
@@ -998,17 +1001,18 @@ namespace Serein.NodeFlow.Services
sb.AppendCode(1, $"}}");
}
public class SereinGlobalDataInfo
private class SereinGlobalDataInfo
{
/// <summary>
/// 全局数据节点
/// </summary>
public SingleGlobalDataNode Node { get; set; }
public required SingleGlobalDataNode Node { get; set; }
/// <summary>
/// 全局数据的来源节点
/// </summary>
public IFlowNode DataSourceNode { get; set; }
public required IFlowNode DataSourceNode { get; set; }
/// <summary>
/// 全局数据的键名

View File

@@ -14,6 +14,10 @@ namespace Serein.NodeFlow.Services
/// </summary>
public class FlowLibraryService
{
/// <summary>
/// 构造函数,初始化流程依赖
/// </summary>
/// <param name="flowEnvironment"></param>
public FlowLibraryService(IFlowEnvironment flowEnvironment)
{
this.flowEnvironment = flowEnvironment;
@@ -35,10 +39,12 @@ namespace Serein.NodeFlow.Services
/// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
/// </summary>
/// <param name="libraryfilePath"></param>
/// <param name="baseLibraryPath"></param>
/// <returns></returns>
private bool CheckBaseLibrary(string libraryfilePath, out string baseLibraryPath)
{
var dir = Path.GetDirectoryName(libraryfilePath); // 获取目录路径
ArgumentNullException.ThrowIfNullOrWhiteSpace(dir);
var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);
if (!Path.Exists(sereinFlowBaseLibraryPath))
{
@@ -146,7 +152,7 @@ namespace Serein.NodeFlow.Services
/// </summary>
/// <param name="assemblyName">程序集名称</param>
/// <param name="methodName">方法名称</param>
/// <param name="md">返回的方法描述</param>
/// <param name="methodInfo">返回的方法描述</param>
/// <returns>是否获取成功</returns>
public bool TryGetMethodInfo(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodInfo methodInfo)
{

View File

@@ -2,6 +2,7 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using Serein.NodeFlow.Model.Nodes;
using System.Diagnostics.CodeAnalysis;
namespace Serein.NodeFlow.Services
{
@@ -13,6 +14,11 @@ namespace Serein.NodeFlow.Services
private readonly IFlowEnvironment environment;
private readonly FlowLibraryService flowLibraryService;
/// <summary>
/// 流程模型服务构造函数
/// </summary>
/// <param name="environment"></param>
/// <param name="flowLibraryService"></param>
public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService)
{
this.environment = environment;
@@ -35,72 +41,156 @@ namespace Serein.NodeFlow.Services
/// </summary>
private List<SingleFlipflopNode> FlipflopNodes { get; } = [];
/// <summary>
/// 获取节点模型
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public IFlowNode? GetNodeModel(string guid)
{
NodeModels.TryGetValue(guid, out var nodeModel);
return nodeModel;
}
/// <summary>
/// 获取画布模型
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public FlowCanvasDetails? GetCanvasModel(string guid)
{
FlowCanvass.TryGetValue(guid, out var nodeModel);
return nodeModel;
}
public bool TryGetNodeModel(string guid, out IFlowNode flowNode)
/// <summary>
/// 尝试获取节点模型
/// </summary>
/// <param name="guid"></param>
/// <param name="flowNode"></param>
/// <returns></returns>
public bool TryGetNodeModel(string guid, [NotNullWhen(true)] out IFlowNode? flowNode)
{
return NodeModels.TryGetValue(guid, out flowNode!);
}
public bool TryGetCanvasModel(string guid, out FlowCanvasDetails flowCanvas)
/// <summary>
/// 尝试获取画布模型
/// </summary>
/// <param name="guid"></param>
/// <param name="flowCanvas"></param>
/// <returns></returns>
public bool TryGetCanvasModel(string guid, [NotNullWhen(true)] out FlowCanvasDetails? flowCanvas)
{
return FlowCanvass.TryGetValue(guid, out flowCanvas!); ;
if(FlowCanvass.TryGetValue(guid, out var details))
{
flowCanvas = details;
return true;
}
flowCanvas = details;
return false;
}
/// <summary>
/// 检查是否包含节点模型
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public bool ContainsNodeModel(string guid)
{
return NodeModels.ContainsKey(guid);
}
/// <summary>
/// 检查是否包含画布模型
/// </summary>
/// <param name="guid"></param>
/// <returns></returns>
public bool ContainsCanvasModel(string guid)
{
return FlowCanvass.ContainsKey(guid);
}
/// <summary>
/// 添加节点模型
/// </summary>
/// <param name="flowNode"></param>
/// <returns></returns>
public bool AddNodeModel(IFlowNode flowNode)
{
ArgumentNullException.ThrowIfNull(flowNode);
ArgumentNullException.ThrowIfNull(flowNode.Guid);
return NodeModels.TryAdd(flowNode.Guid, flowNode);
}
/// <summary>
/// 添加画布模型
/// </summary>
/// <param name="flowCanvasDetails"></param>
/// <returns></returns>
public bool AddCanvasModel(FlowCanvasDetails flowCanvasDetails)
{
ArgumentNullException.ThrowIfNull(flowCanvasDetails);
ArgumentNullException.ThrowIfNull(flowCanvasDetails.Guid);
return FlowCanvass.TryAdd(flowCanvasDetails.Guid, flowCanvasDetails);
}
/// <summary>
/// 移除节点模型
/// </summary>
/// <param name="flowNode"></param>
/// <returns></returns>
public bool RemoveNodeModel(IFlowNode flowNode)
{
ArgumentNullException.ThrowIfNull(flowNode.Guid);
return NodeModels.Remove(flowNode.Guid);
}
/// <summary>
/// 移除画布模型
/// </summary>
/// <param name="flowCanvasDetails"></param>
/// <returns></returns>
public bool RemoveCanvasModel(FlowCanvasDetails flowCanvasDetails)
{
ArgumentNullException.ThrowIfNull(flowCanvasDetails.Guid);
return FlowCanvass.Remove(flowCanvasDetails.Guid);
}
/// <summary>
/// 获取所有节点模型
/// </summary>
/// <returns></returns>
public List<IFlowNode> GetAllNodeModel() => [.. NodeModels.Values];
/// <summary>
/// 获取指定画布上的所有节点模型
/// </summary>
/// <param name="canvasGuid"></param>
/// <returns></returns>
public List<IFlowNode> GetAllNodeModel(string canvasGuid) =>
NodeModels.Values.Where(x => x.CanvasDetails.Guid == canvasGuid).ToList();
/// <summary>
/// 获取所有画布模型
/// </summary>
/// <returns></returns>
public List<FlowCanvasDetails> GetAllCanvasModel() => [.. FlowCanvass.Values];
/// <summary>
/// 检查是否存在画布模型
/// </summary>
/// <returns></returns>
public bool IsExsitCanvas()
{
return FlowCanvass.Count > 0;
}
/// <summary>
/// 检查指定画布上是否存在节点模型
/// </summary>
/// <param name="canvasGuid"></param>
/// <returns></returns>
public bool IsExsitNodeOnCanvas(string canvasGuid)
{
if (!FlowCanvass.TryGetValue(canvasGuid, out var flowCanvasDetails))

View File

@@ -1,5 +1,5 @@
using Serein.Library.Api;
using Serein.NodeFlow.Model.Operation;
using Serein.NodeFlow.Model.Operations;
using System;
using System.Collections.Generic;
using System.Linq;

View File

@@ -17,11 +17,22 @@ namespace Serein.NodeFlow.Tool
private AssemblyLoadContext context;
private Dictionary<string, Type> dicTypes = new Dictionary<string, Type>();
/// <summary>
/// 程序集加载器构造函数
/// </summary>
/// <param name="basePath"></param>
public AssemblyLoader(string basePath)
{
_basePath = basePath;
}
/// <summary>
/// 加载指定的类型
/// </summary>
/// <param name="dllFileName"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public Type Load(string dllFileName, string typeName)
{
context = new AssemblyLoadContext(dllFileName);

View File

@@ -12,6 +12,9 @@ namespace Serein.NodeFlow.Tool
{
private readonly HashSet<MetadataReference> _references = new HashSet<MetadataReference>();
/// <summary>
/// 构造一个新的动态编译器实例。
/// </summary>
public DynamicCompiler()
{
// 默认添加当前 AppDomain 加载的所有程序集
@@ -110,10 +113,6 @@ namespace Serein.NodeFlow.Tool
}
public void Save()
{
}

View File

@@ -33,7 +33,11 @@ namespace Serein.NodeFlow.Tool
/// </summary>
public override Encoding Encoding => Encoding.UTF8;
/// <summary>
/// 重写Write方法处理单个字符的写入
/// </summary>
/// <param name="value"></param>
public override void Write(char value)
{
stringWriter.Write(value);
@@ -43,6 +47,10 @@ namespace Serein.NodeFlow.Tool
}
}
/// <summary>
/// 重写Write方法处理字符串的写入
/// </summary>
/// <param name="value"></param>
public override void Write(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return;
@@ -53,6 +61,10 @@ namespace Serein.NodeFlow.Tool
}
}
/// <summary>
/// 重写WriteLine方法处理字符串的换行写入
/// </summary>
/// <param name="value"></param>
public override void WriteLine(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return;

View File

@@ -103,8 +103,7 @@ namespace Serein.NodeFlow.Tool
/// <summary>
/// 加载Windows类库
/// </summary>
/// <param name="path"></param>
/// <param name="isRecurrence">是否递归加载</param>
/// <param name="file"></param>
private static bool LoadWindowsLibrarie(string file)
{
IntPtr hModule = IntPtr.Zero;

View File

@@ -1,5 +1,6 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using System.Collections.Concurrent;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@@ -7,6 +8,9 @@ using System.Reflection;
namespace Serein.NodeFlow.Tool;
/// <summary>
/// 节点方法描述帮助类
/// </summary>
public static class NodeMethodDetailsHelper
{
@@ -25,15 +29,16 @@ public static class NodeMethodDetailsHelper
/// <param name="type">方法所属的类型</param>
/// <param name="methodInfo">方法信息</param>
/// <param name="assemblyName">方法所属的程序集名称</param>
/// <param name="outMethodInfo">传出的方法信息</param>
/// <param name="methodDetails">创建的方法描述,用来生成节点信息</param>
/// <param name="delegateDetails">方法对应的Emit动态委托</param>
/// <returns>指示是否创建成功</returns>
public static bool TryCreateDetails(Type type,
MethodInfo methodInfo,
string assemblyName,
[MaybeNullWhen(false)] out MethodInfo outMethodInfo,
[MaybeNullWhen(false)] out MethodDetails methodDetails,
[MaybeNullWhen(false)] out DelegateDetails delegateDetails)
[NotNullWhen(true)] out MethodInfo? outMethodInfo,
[NotNullWhen(true)] out MethodDetails? methodDetails,
[NotNullWhen(true)] out DelegateDetails? delegateDetails)
{
@@ -60,7 +65,7 @@ public static class NodeMethodDetailsHelper
Type? returnType;
bool isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
bool isAsync = EmitHelper.IsGenericTask(methodInfo.ReturnType, out var taskResult);
bool isStatic = methodInfo.IsStatic;
@@ -172,6 +177,12 @@ public static class NodeMethodDetailsHelper
return true;
}
/// <summary>
/// 获取方法签名
/// </summary>
/// <param name="method"></param>
/// <returns></returns>
public static string GetMethodSignature(MethodInfo method)
{
if (method == null) return string.Empty;
@@ -184,28 +195,8 @@ public static class NodeMethodDetailsHelper
return $"{methodName}({parameters})";
//return $"{methodName}({parameters}) : {returnType}";
}
public static bool IsGenericTask(Type returnType, out Type? taskResult)
{
// 判断是否为 Task 类型或泛型 Task<T>
if (returnType == typeof(Task))
{
taskResult = null;
return true;
}
else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>))
{
// 获取泛型参数类型
Type genericArgument = returnType.GetGenericArguments()[0];
taskResult = genericArgument;
return true;
}
else
{
taskResult = null;
return false;
}
}
private static ConcurrentDictionary<string, (object, MethodInfo)> ConvertorInstance =[];
@@ -231,6 +222,7 @@ public static class NodeMethodDetailsHelper
}
#endregion
#region
#if false
else if (it.GetCustomAttribute<BindConvertorAttribute>() is BindConvertorAttribute attribute2 && attribute2 is not null)
{
paremType = attribute2.EnumType;
@@ -262,6 +254,8 @@ public static class NodeMethodDetailsHelper
return ed;
}
#endif
#endregion
#region
else

View File

@@ -7,11 +7,20 @@ using System.Reflection.Emit;
namespace Serein.NodeFlow.Tool
{
/// <summary>
/// 动态创建对象的帮助类,支持根据属性字典创建对象并设置属性值。
/// </summary>
public class ObjDynamicCreateHelper
{
// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式)
static Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
/// <summary>
/// 根据属性字典和类型名称创建对象实例,并设置属性值。
/// </summary>
/// <param name="properties"></param>
/// <param name="typeName"></param>
/// <returns></returns>
public static object Resolve(Dictionary<string, object> properties, string typeName)
{
var obj = CreateObjectWithProperties(properties, typeName);

View File

@@ -16,13 +16,20 @@ namespace Serein.Extend.NewtonsoftJson
/// </summary>
public sealed class NewtonsoftJsonProvider : IJsonProvider
{
private JsonSerializerSettings settings;
private JsonSerializerSettings? settings;
/// <summary>
/// 基于Newtonsoft.Json的JSON门户实现
/// </summary>
public NewtonsoftJsonProvider()
{
}
/// <summary>
/// 基于Newtonsoft.Json的JSON门户实现
/// </summary>
/// <param name="jsonType"></param>
public NewtonsoftJsonProvider(JsonType jsonType)
{
settings = jsonType switch
@@ -48,39 +55,78 @@ namespace Serein.Extend.NewtonsoftJson
this.settings = settings;
}
/// <summary>
/// 反序列化JSON文本为指定类型的对象。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="jsonText"></param>
/// <returns></returns>
public T? Deserialize<T>(string jsonText)
{
return JsonConvert.DeserializeObject<T>(jsonText, settings);
}
/// <summary>
/// 反序列化JSON文本为指定类型的对象。
/// </summary>
/// <param name="jsonText"></param>
/// <param name="type"></param>
/// <returns></returns>
public object? Deserialize(string jsonText, Type type)
{
return JsonConvert.DeserializeObject(jsonText, type, settings);
}
/// <summary>
/// 序列化对象为JSON文本。
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public string Serialize(object obj)
{
return JsonConvert.SerializeObject(obj, settings);
}
/// <summary>
/// 将JSON文本解析为IJsonToken对象。
/// </summary>
/// <param name="json"></param>
/// <returns></returns>
public IJsonToken Parse(string json)
{
var token = JToken.Parse(json);
return new NewtonsoftJsonToken(token);
}
public IJsonToken CreateObject(IDictionary<string, object> values = null)
/// <summary>
/// 创建一个新的JSON对象。
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public IJsonToken CreateObject(IDictionary<string, object>? values = null)
{
var jobj = values != null ? JObject.FromObject(values) : new JObject();
return new NewtonsoftJsonToken(jobj);
}
public IJsonToken CreateArray(IEnumerable<object> values = null)
/// <summary>
/// 创建一个新的JSON数组。
/// </summary>
/// <param name="values"></param>
/// <returns></returns>
public IJsonToken CreateArray(IEnumerable<object>? values = null)
{
var jarr = values != null ? JArray.FromObject(values) : new JArray();
return new NewtonsoftJsonToken(jarr);
}
/// <summary>
/// 将对象转换为IJsonToken。
/// </summary>
/// <param name="obj"></param>
/// <returns></returns>
public IJsonToken FromObject(object obj)
{
var token = JToken.FromObject(obj);

View File

@@ -1,5 +1,6 @@
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using System.Diagnostics.CodeAnalysis;
namespace Serein.Extend.NewtonsoftJson
{
@@ -10,14 +11,25 @@ namespace Serein.Extend.NewtonsoftJson
{
private readonly JToken _token;
/// <summary>
/// 使用JToken初始化一个新的NewtonsoftJsonToken实例。
/// </summary>
/// <param name="token"></param>
/// <exception cref="ArgumentNullException"></exception>
public NewtonsoftJsonToken(JToken token)
{
_token = token ?? throw new ArgumentNullException(nameof(token));
}
public bool TryGetValue(string name, out IJsonToken token)
/// <summary>
/// 尝试获取指定名称的属性值。
/// </summary>
/// <param name="name"></param>
/// <param name="token"></param>
/// <returns></returns>
public bool TryGetValue(string name, [NotNullWhen(true)] out IJsonToken? token)
{
if (_token is JObject obj && obj.TryGetValue(name, out JToken value))
if (_token is JObject obj && obj.TryGetValue(name, out JToken? value))
{
token = new NewtonsoftJsonToken(value);
return true;
@@ -28,14 +40,14 @@ namespace Serein.Extend.NewtonsoftJson
public IJsonToken? GetValue(string name)
{
if (_token is JObject obj && obj.TryGetValue(name, out JToken value))
if (_token is JObject obj && obj.TryGetValue(name, out JToken? value))
{
return new NewtonsoftJsonToken(value);
}
return null;
}
public string GetString() => _token.Type == JTokenType.Null ? null : _token.ToString();
public string GetString() => (_token.Type == JTokenType.Null ? null : _token.ToString()) ?? string.Empty;
public int GetInt32() => _token.Value<int>();
@@ -50,10 +62,28 @@ namespace Serein.Extend.NewtonsoftJson
throw new InvalidOperationException("当前Token不是数组类型。");
}
/// <summary>
/// 将当前JSON Token转换为指定类型的对象。
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
#pragma warning disable CS8603 // 可能返回 null 引用。
public T ToObject<T>() => _token.ToObject<T>();
#pragma warning restore CS8603 // 可能返回 null 引用。
/// <summary>
/// 将当前JSON Token转换为指定类型的对象。
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
#pragma warning disable CS8603 // 可能返回 null 引用。
public object ToObject(Type type) => _token.ToObject(type);
#pragma warning restore CS8603 // 可能返回 null 引用。
/// <summary>
/// 返回当前JSON Token的字符串表示形式。
/// </summary>
/// <returns></returns>
public override string ToString() => _token.ToString();
}
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net462</TargetFrameworks>
<TargetFrameworks>net8.0;</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
@@ -16,6 +16,22 @@
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net8.0|AnyCPU'">
<NoWarn>1701;1702;CS8766</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|net462|AnyCPU'">
<NoWarn>1701;1702;CS8766</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net8.0|AnyCPU'">
<NoWarn>1701;1702;CS8766</NoWarn>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|net462|AnyCPU'">
<NoWarn>1701;1702;CS8766</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Library\Serein.Library.csproj" />
</ItemGroup>

View File

@@ -40,20 +40,25 @@ namespace Serein.Library
/// 标识一个类中的某些字段需要生成相应代码
/// </summary>
[AttributeUsage(AttributeTargets.Class, Inherited = true)]
public sealed class NodePropertyAttribute : Attribute
public sealed class FlowDataPropertyAttribute : Attribute
{
/// <summary>
/// <para>属性路径</para>
/// <para>CustomNode : 自定义节点</para>
/// </summary>
public NodeValuePath ValuePath = NodeValuePath.None;
/// <summary>
/// 表示该类是否为节点实现类
/// </summary>
public bool IsNodeImp = false;
}
/// <summary>
/// 自动生成环境的属性
/// </summary>
[AttributeUsage(AttributeTargets.Field, Inherited = true)]
public sealed class PropertyInfoAttribute : Attribute
public sealed class DataInfoAttribute : Attribute
{
/// <summary>
/// 是否通知远程环境(如果在远程环境下)

View File

@@ -4,24 +4,25 @@ using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
namespace Serein.Library.NodeGenerator
{
/// <summary>
/// 一个增量源生成器,用于为带有自定义 MyClassAttribute 特性的类中的字段生成带有自定义 set 行为的属性。
/// 增量源生成器
/// </summary>
[Generator]
public class MyPropertyGenerator : IIncrementalGenerator
public class FlowDataPropertyGenerator : IIncrementalGenerator
{
internal static NodePropertyAttribute NodeProperty = new NodePropertyAttribute();
internal static PropertyInfoAttribute PropertyInfo = new PropertyInfoAttribute();
internal static FlowDataPropertyAttribute FlowDataProperty = new FlowDataPropertyAttribute();
internal static DataInfoAttribute DataInfo = new DataInfoAttribute();
/// <summary>
/// 初始化生成器,定义需要执行的生成逻辑。
@@ -29,12 +30,16 @@ namespace Serein.Library.NodeGenerator
/// <param name="context">增量生成器的上下文,用于注册生成逻辑。</param>
public void Initialize(IncrementalGeneratorInitializationContext context)
{
/*
* //Debugger.Launch();
CreateSyntaxProvider :
SemanticModel : (SemanticModel)
AddSource : 使 AddSource
*/
// 通过 SyntaxProvider 查找所有带有任意特性修饰的类声明语法节点
var classDeclarations = context.SyntaxProvider
.CreateSyntaxProvider(
// 定义要查找的语法节点类型,这里我们只关心类声明 (ClassDeclarationSyntax) 并且它们有至少一个特性 (Attribute)
@@ -50,7 +55,7 @@ namespace Serein.Library.NodeGenerator
// 检查类的特性列表,看看是否存在 MyClassAttribute
if (classDeclaration.AttributeLists
.SelectMany(attrList => attrList.Attributes)
.Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == nameof(NodePropertyAttribute)))
.Any(attr => semanticModel.GetSymbolInfo(attr).Symbol?.ContainingType.Name == nameof(FlowDataPropertyAttribute)))
{
var classSymbol = semanticModel.GetDeclaredSymbol(classDeclaration); // 获取类的符号
var classInfo = classSymbol.BuildCacheOfClass();
@@ -77,7 +82,7 @@ namespace Serein.Library.NodeGenerator
// 获取类的命名空间和类名
var namespaceName = GetNamespace(classSyntax);
var className = classSyntax.Identifier.Text;
Debug.WriteLine($"Generator Class Name : {className}");
// 生成属性代码
var generatedCode = GenerateProperties(classSyntax, result.classInfo, namespaceName, className);
@@ -93,6 +98,7 @@ namespace Serein.Library.NodeGenerator
/// </summary>
/// <param name="classSyntax">类的语法树节点。</param>
/// <param name="namespaceName">类所在的命名空间。</param>
/// <param name="classInfo">。</param>
/// <param name="className">类的名称。</param>
/// <returns>生成的 C# 属性代码。</returns>
private string GenerateProperties(ClassDeclarationSyntax classSyntax,
@@ -144,9 +150,8 @@ namespace Serein.Library.NodeGenerator
var fieldType = field.Declaration.Type.ToString(); // 获取字段类型
var propertyName = field.ToPropertyName(); // 转为合适的属性名称
var attributeInfo = fieldKV.Value; // 缓存的特性信息
var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段
var isVerify = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsVerify), value => bool.Parse(value)); // 是否为保护字段
var isProtection = attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsProtection), value => bool.Parse(value)); // 是否为保护字段
var isVerify = attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsVerify), value => bool.Parse(value)); // 是否为保护字段
//sb.AppendLine(leadingTrivia);
sb.AppendLine($" partial void On{propertyName}Changed({fieldType} oldValue, {fieldType} newValue);");
@@ -194,7 +199,7 @@ namespace Serein.Library.NodeGenerator
//sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); ");
sb.AppendLine($" On{propertyName}Changed(value);");
sb.AppendLine($" On{propertyName}Changed(__oldValue, value);");
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsPrint), value => bool.Parse(value)))
if (attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsPrint), value => bool.Parse(value)))
{
sb.AddCode(5, $"Console.WriteLine({fieldName});");
} // 是否打印
@@ -206,7 +211,7 @@ namespace Serein.Library.NodeGenerator
// NodeValuePath.Method 节点 → 方法描述
// NodeValuePath.Parameter 节点 → 方法描述 → 参数描述
if (attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsNotification), value => bool.Parse(value))) // 是否通知
if (attributeInfo.Search(nameof(DataInfo), nameof(DataInfo.IsNotification), value => bool.Parse(value))) // 是否通知
{
if (classInfo.ExitsPath(nameof(NodeValuePath.Node))) // 节点 or 自定义节点
@@ -250,33 +255,46 @@ namespace Serein.Library.NodeGenerator
}
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 略");
sb.AppendLine(" /// <para>此事件为自动生成</para>");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
var isNodeImp = false;
sb.AppendLine(" protected bool SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
sb.AppendLine(" { ");
sb.AppendLine(" if (Equals(storage, value)) ");
sb.AppendLine(" { ");
sb.AppendLine(" return false; ");
sb.AppendLine(" } ");
sb.AppendLine(" ");
sb.AppendLine(" storage = value; ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" return true; ");
sb.AppendLine(" } ");
if (classInfo.TryGetValue(nameof(FlowDataPropertyAttribute), out var values)
&& values.TryGetValue(nameof(FlowDataPropertyAttribute.IsNodeImp), out object data)
&& bool.TryParse(data.ToString(), out var isNodeImpTemp)
&& isNodeImpTemp)
{
isNodeImp = true;
}
sb.AppendLine(" public void OnPropertyChanged(string propertyName) => ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" ");
sb.AppendLine(" ");
sb.AppendLine(" ");
if (!isNodeImp)
{
sb.AppendLine(" /// <summary>");
sb.AppendLine(" /// 略");
sb.AppendLine(" /// <para>此事件为自动生成</para>");
sb.AppendLine(" /// </summary>");
sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;");
// 生成变量修改
sb.AppendLine(" protected bool SetProperty<T>(ref T storage, T value, [System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
sb.AppendLine(" { ");
sb.AppendLine(" if (Equals(storage, value)) ");
sb.AppendLine(" { ");
sb.AppendLine(" return false; ");
sb.AppendLine(" } ");
sb.AppendLine(" ");
sb.AppendLine(" storage = value; ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" return true; ");
sb.AppendLine(" } ");
sb.AppendLine(" public void OnPropertyChanged(string propertyName) => ");
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
sb.AppendLine(" ");
sb.AppendLine(" ");
sb.AppendLine(" ");
}
//sb.AppendLine(" /// <summary> ");
//sb.AppendLine(" /// 略 ");
@@ -302,38 +320,6 @@ namespace Serein.Library.NodeGenerator
}
/*private void ModifyValue(string path, string value)
{
if (string.IsNullOrWhiteSpace(path))
{
return;
}
else if (path.Equals(nameof(MaxChildrenCount), StringComparison.OrdinalIgnoreCase))
{
if (typeof(int) == typeof(string))
{
this.MaxChildrenCount = value;
}
else
{
this.MaxChildrenCount = ConvertHelper.ValueParse<int>(value);
}
}
else if (path.Equals(nameof(Guid), StringComparison.OrdinalIgnoreCase))
{
if (typeof(string) == typeof(string))
{
this.Guid = value;
}
else
{
this.Guid = ConvertHelper.ValueParse<string>(value);
}
}
}*/
/// <summary>
/// 获取类所在的命名空间。
/// </summary>
@@ -347,21 +333,16 @@ namespace Serein.Library.NodeGenerator
}
private void SetterIsProtection()
{
}
private void SetterNotIsProtection()
{
}
}
/// <summary>
/// 扩展方法,用于处理 XML 文档注释中的 summary 标签
/// </summary>
public static class DocumentationCommentExtensions
{
@@ -431,10 +412,18 @@ namespace Serein.Library.NodeGenerator
}
}
/// <summary>
/// MyAttributeResolver
/// </summary>
public static class MyAttributeResolver
{
/// <summary>
/// 构建类的特性缓存信息
/// </summary>
/// <param name="classSymbol"></param>
/// <returns></returns>
public static Dictionary<string, Dictionary<string, object>> BuildCacheOfClass(this INamedTypeSymbol classSymbol)
{
Dictionary<string, Dictionary<string, object>> attributesOfClass = new Dictionary<string, Dictionary<string, object>>();
@@ -452,10 +441,19 @@ namespace Serein.Library.NodeGenerator
{
var key = cata.Key;
var value = cata.Value.Value;
if (nameof(NodePropertyAttribute).Equals(attributeName))
if (nameof(FlowDataPropertyAttribute).Equals(attributeName))
{
string literal = Enum.GetName(typeof(NodeValuePath), cata.Value.Value);
attributeInfo.Add(key, literal);
if(cata.Key == nameof(FlowDataPropertyAttribute.ValuePath))
{
string literal = Enum.GetName(typeof(NodeValuePath), cata.Value.Value);
attributeInfo.Add(key, literal);
}
else if (cata.Key == nameof(FlowDataPropertyAttribute.IsNodeImp))
{
string literal = cata.Value.Value.ToString();
attributeInfo.Add(key, literal);
}
}
else
{
@@ -463,8 +461,8 @@ namespace Serein.Library.NodeGenerator
}
//Console.WriteLine("key:" + cata.Key);// 类特性的属性名
//Console.WriteLine("value:" + cata.Value.Value); // 类特性的属性值
Debug.WriteLine("key : " + cata.Key);// 类特性的属性名
Debug.WriteLine("value : " + cata.Value.Value); // 类特性的属性值
}
}
return attributesOfClass;
@@ -632,15 +630,21 @@ namespace Serein.Library.NodeGenerator
}
/// <summary>
/// 检查类信息中是否存在指定的路径
/// </summary>
/// <param name="classInfo"></param>
/// <param name="valuePath"></param>
/// <returns></returns>
public static bool ExitsPath(this Dictionary<string, Dictionary<string, object>> classInfo, string valuePath)
{
if (!classInfo.TryGetValue(nameof(NodePropertyAttribute), out var keyValuePairs))
if (!classInfo.TryGetValue(nameof(FlowDataPropertyAttribute), out var keyValuePairs))
{
return false;
}
if (!keyValuePairs.TryGetValue(nameof(MyPropertyGenerator.NodeProperty.ValuePath), out var value))
if (!keyValuePairs.TryGetValue(nameof(FlowDataPropertyGenerator.FlowDataProperty.ValuePath), out var value))
{
return false;
}

Some files were not shown because too many files have changed in this diff Show More