diff --git a/FlowStartTool/FlowEnv.cs b/FlowStartTool/FlowEnv.cs index ad624e2..aa68123 100644 --- a/FlowStartTool/FlowEnv.cs +++ b/FlowStartTool/FlowEnv.cs @@ -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线程上下文信息 diff --git a/Library/Api/IEnumConvertor.cs b/Library/Api/IEnumConvertor.cs index d900d9b..4f64e6c 100644 --- a/Library/Api/IEnumConvertor.cs +++ b/Library/Api/IEnumConvertor.cs @@ -7,6 +7,11 @@ /// public interface IEnumConvertor { + /// + /// 将枚举值转换为指定类型的值 + /// + /// + /// TValue Convertor(TEnum e); } diff --git a/Library/Api/IFlowContext.cs b/Library/Api/IFlowContext.cs index 2645b0a..3f2ffaa 100644 --- a/Library/Api/IFlowContext.cs +++ b/Library/Api/IFlowContext.cs @@ -252,11 +252,20 @@ namespace Serein.Library.Api /// public string Result { get; private set; } + /// + /// 上传当前节点的执行状态和结果信息。 + /// + /// public void UploadState(RunState runState) { State = runState; TS = DateTime.Now - StateTime; } + + /// + /// 上传当前节点的执行结果值。 + /// + /// public void UploadResultValue(object value = null) { if(value is null) @@ -269,6 +278,11 @@ namespace Serein.Library.Api Result = $"{type.FullName}::{value}"; } } + + /// + /// 上传当前节点的执行参数信息。 + /// + /// public void UploadParameters(object[] values = null) { if (values is null) @@ -282,6 +296,10 @@ namespace Serein.Library.Api } } + /// + /// 返回当前节点的执行信息字符串,包含状态、耗时和结果。 + /// + /// public override string ToString() { return $"[{State}]{TS.TotalSeconds:0.000}ms : {Result}"; diff --git a/Library/Api/IFlowControl.cs b/Library/Api/IFlowControl.cs index 3031921..65494fa 100644 --- a/Library/Api/IFlowControl.cs +++ b/Library/Api/IFlowControl.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; namespace Serein.Library.Api { +#nullable enable /// /// 流程运行接口 /// @@ -24,7 +25,7 @@ namespace Serein.Library.Api /// /// /// 用于每次启动时,重置IOC后默认注册某些类型 - void UseExternalIOC(ISereinIOC ioc, Action setDefultMemberOnReset = null); + void UseExternalIOC(ISereinIOC ioc, Action? setDefultMemberOnReset = null); /// /// 开始运行流程 diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index d8eaa04..94e0cbc 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -153,6 +153,9 @@ namespace Serein.Library.Api /// public class ProjectLoadedEventArgs : FlowEventArgs { + /// + /// 项目加载完成事件参数 + /// public ProjectLoadedEventArgs() { } @@ -163,6 +166,10 @@ namespace Serein.Library.Api /// public class ProjectSavingEventArgs : FlowEventArgs { + /// + /// 项目保存事件参数 + /// + /// public ProjectSavingEventArgs(SereinProjectData projectData) { ProjectData = projectData; @@ -179,6 +186,10 @@ namespace Serein.Library.Api /// public class LoadDllEventArgs : FlowEventArgs { + /// + /// 加载了DLL外部依赖事件参数 + /// + /// public LoadDllEventArgs(FlowLibraryInfo nodeLibraryInfo) { this.NodeLibraryInfo = nodeLibraryInfo; @@ -194,6 +205,9 @@ namespace Serein.Library.Api /// public class RemoteDllEventArgs : FlowEventArgs { + /// + /// 移除了DLL外部依赖事件参数 + /// public RemoteDllEventArgs() { } @@ -270,6 +284,9 @@ namespace Serein.Library.Api } + /// + /// 连接关系所在的画布Guid + /// public string CanvasGuid { get; } /// @@ -309,11 +326,18 @@ namespace Serein.Library.Api /// public class CanvasCreateEventArgs : FlowEventArgs { + /// + /// 画布添加事件参数 + /// + /// public CanvasCreateEventArgs(FlowCanvasDetails model) { Model = model; } + /// + /// 画布 + /// public FlowCanvasDetails Model { get; } } @@ -322,11 +346,18 @@ namespace Serein.Library.Api /// public class CanvasRemoveEventArgs : FlowEventArgs { + /// + /// 画布移除事件参数 + /// + /// public CanvasRemoveEventArgs(string canvasGuid) { CanvasGuid = canvasGuid; } + /// + /// 所处画布Guid + /// public string CanvasGuid { get; } } @@ -360,10 +391,6 @@ namespace Serein.Library.Api /// 在UI上的位置 /// public PositionOfUI Position { get; private set; } - /// - /// 容器 - /// - //public string RegeionGuid { get; private set; } } /// @@ -371,12 +398,20 @@ namespace Serein.Library.Api /// public class NodeRemoveEventArgs : FlowEventArgs { + /// + /// 被移除节点事件参数 + /// + /// + /// public NodeRemoveEventArgs(string canvasGuid, string nodeGuid) { CanvasGuid = canvasGuid; this.NodeGuid = nodeGuid; } + /// + /// 被移除节点所在的画布Guid + /// public string CanvasGuid { get; } /// @@ -390,6 +425,12 @@ namespace Serein.Library.Api /// public class NodePlaceEventArgs : FlowEventArgs { + /// + /// 节点放置事件参数 + /// + /// + /// + /// public NodePlaceEventArgs(string canvasGuid, string nodeGuid, string containerNodeGuid) { CanvasGuid = canvasGuid; @@ -397,6 +438,9 @@ namespace Serein.Library.Api ContainerNodeGuid = containerNodeGuid; } + /// + /// 画布Guid + /// public string CanvasGuid { get; } /// @@ -414,6 +458,12 @@ namespace Serein.Library.Api /// public class NodeTakeOutEventArgs : FlowEventArgs { + /// + /// 节点取出事件参数 + /// + /// + /// + /// public NodeTakeOutEventArgs(string canvasGuid, string containerNodeGuid, string nodeGuid) { CanvasGuid = canvasGuid; @@ -421,6 +471,9 @@ namespace Serein.Library.Api ContainerNodeGuid = containerNodeGuid; } + /// + /// 所在画布Guid + /// public string CanvasGuid { get; } /// @@ -438,9 +491,17 @@ namespace Serein.Library.Api - + /// + /// 起始节点发生了变化 + /// public class StartNodeChangeEventArgs : FlowEventArgs { + /// + /// 起始节点发生了变化事件参数 + /// + /// + /// + /// public StartNodeChangeEventArgs(string canvasGuid, string oldNodeGuid, string newNodeGuid) { CanvasGuid = canvasGuid; @@ -448,6 +509,9 @@ namespace Serein.Library.Api this.NewNodeGuid = newNodeGuid; ; } + /// + /// 所在画布Guid + /// public string CanvasGuid { get; } /// @@ -515,6 +579,11 @@ namespace Serein.Library.Api /// public class NodeInterruptStateChangeEventArgs : FlowEventArgs { + /// + /// 节点中断状态改变事件参数 + /// + /// + /// public NodeInterruptStateChangeEventArgs(string nodeGuid,bool isInterrupt) { NodeGuid = nodeGuid; @@ -526,14 +595,19 @@ namespace Serein.Library.Api /// 中断的节点Guid /// public string NodeGuid { get;} + /// + /// 是否中断 + /// public bool IsInterrupt { get;} - // public InterruptClass Class { get;} } /// /// 节点触发了中断事件参数 /// public class InterruptTriggerEventArgs : FlowEventArgs { + /// + /// 中断触发类型 + /// public enum InterruptTriggerType { /// @@ -550,6 +624,12 @@ namespace Serein.Library.Api Obj, } + /// + /// 中断触发事件参数 + /// + /// + /// + /// public InterruptTriggerEventArgs(string nodeGuid, string expression, InterruptTriggerType type) { this.NodeGuid = nodeGuid; @@ -561,7 +641,13 @@ namespace Serein.Library.Api /// 中断的节点Guid /// public string NodeGuid { get;} + /// + /// 被触发的表达式 + /// public string Expression { get;} + /// + /// 中断触发类型 + /// public InterruptTriggerType Type { get;} } @@ -572,6 +658,9 @@ namespace Serein.Library.Api /// public class IOCMembersChangedEventArgs : FlowEventArgs { + /// + /// IOC成员发生改变的事件类型 + /// public enum EventType { /// @@ -583,12 +672,23 @@ namespace Serein.Library.Api /// Completeuild, } + /// + /// IOC成员发生改变事件参数 + /// + /// + /// public IOCMembersChangedEventArgs(string key, object instance) { this.Key = key; this.Instance = instance; } + /// + /// IOC成员发生改变事件参数 + /// public string Key { get; private set; } + /// + /// IOC成员发生改变事件参数 + /// public object Instance { get; private set; } } @@ -597,38 +697,20 @@ namespace Serein.Library.Api /// public class NodeLocatedEventArgs : FlowEventArgs { + /// + /// 节点需要定位事件参数 + /// + /// public NodeLocatedEventArgs(string nodeGuid) { NodeGuid = nodeGuid; } + /// + /// 节点需要定位事件参数 + /// public string NodeGuid { get; private set; } } -/* /// - /// 节点移动了 - /// - public class NodeMovedEventArgs : FlowEventArgs - { - public NodeMovedEventArgs(string nodeGuid, double x, double y) - { - this.NodeGuid = nodeGuid; - this.X = x; - this.Y = y; - } - /// - /// 节点唯一标识 - /// - public string NodeGuid { get; private set; } - /// - /// 画布上的x坐标 - /// - public double X { get; private set; } - /// - /// 画布上的y坐标 - /// - public double Y { get; private set; } - }*/ - #endregion @@ -729,23 +811,113 @@ namespace Serein.Library.Api /// event EnvOutHandler EnvOutput; + /// + /// 加载了DLL外部依赖事件 + /// + /// public void OnDllLoad(LoadDllEventArgs eventArgs); + + /// + /// 项目加载完成事件 + /// + /// public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs); + + /// + /// 项目准备保存事件 + /// + /// public void OnProjectSaving(ProjectSavingEventArgs eventArgs); + + /// + /// 节点连接关系发生改变事件 + /// + /// public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs); + + /// + /// 画布创建事件 + /// + /// public void OnCanvasCreated(CanvasCreateEventArgs eventArgs); + + /// + /// 画布移除事件 + /// + /// public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs); + + /// + /// 节点创建事件 + /// + /// public void OnNodeCreated(NodeCreateEventArgs eventArgs); + + /// + /// 节点移除事件 + /// + /// public void OnNodeRemoved(NodeRemoveEventArgs eventArgs); + + /// + /// 节点放置事件 + /// + /// public void OnNodePlace(NodePlaceEventArgs eventArgs); + + /// + /// 节点取出事件 + /// + /// public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs); + + /// + /// 起始节点发生了变化事件 + /// + /// public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs); + + /// + /// 流程运行完成事件 + /// + /// public void OnFlowRunComplete(FlowEventArgs eventArgs); + + /// + /// 被监视的对象发生了改变事件 + /// + /// public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs); + + /// + /// 节点中断状态发生了改变事件(开启了中断/取消了中断) + /// + /// public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs); + + /// + /// 触发了中断事件 + /// + /// public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs); + + /// + /// IOC容器成员发生了改变事件 + /// + /// public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs); + + /// + /// 节点需要定位事件 + /// + /// public void OnNodeLocated(NodeLocatedEventArgs eventArgs); + + /// + /// 环境输出信息事件 + /// + /// + /// public void OnEnvOutput(InfoType type, string value); } @@ -864,7 +1036,7 @@ namespace Serein.Library.Api /// 获取当前项目信息 /// /// - Task GetProjectInfoAsync(); + SereinProjectData GetProjectInfoAsync(); #endregion diff --git a/Library/Api/IFlowNode.cs b/Library/Api/IFlowNode.cs index 9987c12..7e3530b 100644 --- a/Library/Api/IFlowNode.cs +++ b/Library/Api/IFlowNode.cs @@ -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; } /// - /// 父节点集合 + /// 前继节点集合 /// Dictionary> PreviousNodes { get;} /// - /// 子节点集合 + /// 后继节点集合 /// Dictionary> SuccessorNodes { get; set; } diff --git a/Library/Api/IJsonProvider.cs b/Library/Api/IJsonProvider.cs index 421d413..203455e 100644 --- a/Library/Api/IJsonProvider.cs +++ b/Library/Api/IJsonProvider.cs @@ -118,14 +118,14 @@ namespace Serein.Library.Api /// /// /// - IJsonToken CreateObject(IDictionary values = null); + IJsonToken CreateObject(IDictionary? values = null); /// /// 创建数组 /// /// /// - IJsonToken CreateArray(IEnumerable values = null); + IJsonToken CreateArray(IEnumerable? values = null); /// /// 将对象转换为JSON Token,自动转换为 JObject/JArray。 diff --git a/Library/Api/INodeContainer.cs b/Library/Api/INodeContainer.cs index ebdc398..30ec24b 100644 --- a/Library/Api/INodeContainer.cs +++ b/Library/Api/INodeContainer.cs @@ -11,7 +11,9 @@ namespace Serein.Library.Api /// public interface INodeContainer { - + /// + /// 容器节点的Guid,与 IFlowNode.Guid 相同 + /// string Guid { get; } /// /// 放置一个节点 diff --git a/Library/Api/IScriptFlowApi.cs b/Library/Api/IScriptFlowApi.cs index dfcf197..5ff9760 100644 --- a/Library/Api/IScriptFlowApi.cs +++ b/Library/Api/IScriptFlowApi.cs @@ -20,20 +20,7 @@ namespace Serein.Library.Api /// IFlowNode NodeModel { get; } - /// - /// 根据索引从入参数据获取数据 - /// - /// - /// - /// - //object GetArgData(IDynamicContext context, int index); - /// - /// 获取流程当前传递的数据 - /// - /// - /// - /// FlowResult GetFlowData(IDynamicContext context); - + /// /// 获取全局数据 /// diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs index 2aade37..e4573c3 100644 --- a/Library/Api/ISereinIoc.cs +++ b/Library/Api/ISereinIoc.cs @@ -108,8 +108,32 @@ namespace Serein.Library.Api /// /// ISereinIOC Run(Action action); + /// + /// 从容器中获取数个类型的实例进行运行 + /// + /// + /// + /// + /// ISereinIOC Run(Action action); + /// + /// 从容器中获取数个类型的实例进行运行 + /// + /// + /// + /// + /// + /// ISereinIOC Run(Action action); + /// + /// 从容器中获取数个类型的实例进行运行 + /// + /// + /// + /// + /// + /// + /// ISereinIOC Run(Action action); } diff --git a/Library/Attributes/AutoRegisterAttribute.cs b/Library/Attributes/AutoRegisterAttribute.cs index 4ae3d34..e905285 100644 --- a/Library/Attributes/AutoRegisterAttribute.cs +++ b/Library/Attributes/AutoRegisterAttribute.cs @@ -16,10 +16,17 @@ namespace Serein.Library [AttributeUsage(AttributeTargets.Class)] public sealed class AutoRegisterAttribute : Attribute { + /// + /// 自动注册特性 + /// + /// public AutoRegisterAttribute(RegisterSequence Class = RegisterSequence.FlowInit) { this.Class = Class; } + /// + /// 注册顺序 + /// public RegisterSequence Class ; } diff --git a/Library/Attributes/BindConvertorAttribute.cs b/Library/Attributes/BindConvertorAttribute.cs index 04b6774..130bdad 100644 --- a/Library/Attributes/BindConvertorAttribute.cs +++ b/Library/Attributes/BindConvertorAttribute.cs @@ -8,9 +8,20 @@ namespace Serein.Library [AttributeUsage(AttributeTargets.Parameter)] public class BindConvertorAttribute : Attribute { + /// + /// 枚举类型 + /// public Type EnumType { get; } + /// + /// 转换器类型 + /// public Type ConvertorType { get; } + /// + /// 绑定转换器特性 + /// + /// + /// public BindConvertorAttribute(Type @enum, Type convertor) { EnumType = @enum; diff --git a/Library/Attributes/BindValueAttribute.cs b/Library/Attributes/BindValueAttribute.cs index e963f6a..52782a8 100644 --- a/Library/Attributes/BindValueAttribute.cs +++ b/Library/Attributes/BindValueAttribute.cs @@ -2,11 +2,21 @@ namespace Serein.Library { + /// + /// 绑定值特性 + /// [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] public class BindValueAttribute : Attribute { + /// + /// 绑定的值 + /// public object Value { get; } + /// + /// 绑定值特性构造函数 + /// + /// public BindValueAttribute(object value) { Value = value; diff --git a/Library/Attributes/DynamicFlowAttribute.cs b/Library/Attributes/DynamicFlowAttribute.cs index 2512a9e..1a8e206 100644 --- a/Library/Attributes/DynamicFlowAttribute.cs +++ b/Library/Attributes/DynamicFlowAttribute.cs @@ -10,6 +10,11 @@ namespace Serein.Library [AttributeUsage(AttributeTargets.Class)] public sealed class DynamicFlowAttribute : Attribute { + /// + /// 动态流程特性构造函数 + /// + /// + /// public DynamicFlowAttribute(string name = "",bool scan = true) { Name = name; diff --git a/Library/Attributes/EnumTypeConvertorAttribute.cs b/Library/Attributes/EnumTypeConvertorAttribute.cs index c0f40c9..69d73b6 100644 --- a/Library/Attributes/EnumTypeConvertorAttribute.cs +++ b/Library/Attributes/EnumTypeConvertorAttribute.cs @@ -9,8 +9,16 @@ namespace Serein.Library [AttributeUsage(AttributeTargets.Parameter)] public class EnumTypeConvertorAttribute : Attribute { + /// + /// 枚举类型 + /// public Type EnumType { get; } + /// + /// 枚举类型转换器特性构造函数 + /// + /// + /// public EnumTypeConvertorAttribute(Type @enum) { if (@enum.IsEnum) diff --git a/Library/Attributes/NodeActionAttribute.cs b/Library/Attributes/NodeActionAttribute.cs index aff2ecd..170231c 100644 --- a/Library/Attributes/NodeActionAttribute.cs +++ b/Library/Attributes/NodeActionAttribute.cs @@ -9,6 +9,13 @@ namespace Serein.Library [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public sealed class NodeActionAttribute : Attribute { + /// + /// 节点行为特性构造函数 + /// + /// + /// + /// + /// public NodeActionAttribute(NodeType methodDynamicType, string methodTips = "", bool scan = true, diff --git a/Library/Enums/ConnectionInvokeType.cs b/Library/Enums/ConnectionInvokeType.cs index ef91b69..ca299ec 100644 --- a/Library/Enums/ConnectionInvokeType.cs +++ b/Library/Enums/ConnectionInvokeType.cs @@ -7,7 +7,7 @@ namespace Serein.Library /// /// 表示了两个节点之间的连接关系,同时表示节点运行完成后,所会执行的下一个节点类型。 - /// public enum ConnectionInvokeType { /// diff --git a/Library/Enums/ParameterValueInputType.cs b/Library/Enums/ParameterValueInputType.cs index 7cee072..ad0b92c 100644 --- a/Library/Enums/ParameterValueInputType.cs +++ b/Library/Enums/ParameterValueInputType.cs @@ -6,9 +6,18 @@ using System.Threading.Tasks; namespace Serein.Library { + /// + /// 参数值输入类型 + /// public enum ParameterValueInputType { + /// + /// 参数值输入类型 - 输入 + /// Input, + /// + /// 参数值输入类型 - 选择器(枚举类型) + /// Select, } } diff --git a/Library/Ex/FlipflopException.cs b/Library/Ex/FlipflopException.cs index 960c68d..94a5747 100644 --- a/Library/Ex/FlipflopException.cs +++ b/Library/Ex/FlipflopException.cs @@ -8,6 +8,9 @@ namespace Serein.Library /// public class FlipflopException: Exception { + /// + /// 触发器取消类型 + /// public enum CancelClass { /// @@ -27,6 +30,13 @@ namespace Serein.Library /// 取消类型 /// public CancelClass Type { get; } + + /// + /// 触发器异常构造函数 + /// + /// + /// + /// public FlipflopException(string message, bool isCancel = true,CancelClass clsss = CancelClass.CancelBranch) :base(message) { IsCancel = isCancel; diff --git a/Library/FlowBaseLibrary.cs b/Library/FlowBaseLibrary.cs index 915f960..cd4d5c0 100644 --- a/Library/FlowBaseLibrary.cs +++ b/Library/FlowBaseLibrary.cs @@ -11,10 +11,23 @@ namespace Serein.Library [DynamicFlow(Name ="[基础功能]")] public static class FlowBaseLibrary { - + + /// + /// 对象透传,直接返回入参的值 + /// + /// + /// [NodeAction(NodeType.Action, "对象透传")] public static object TransmissionObject(object value) => value; + /// + /// 键值对组装,将入参的值与名称组装成一个字典对象 + /// + /// + /// + /// + /// + [NodeAction(NodeType.Action, "键值对组装")] public static Dictionary DictSet(string argNames, params object[] value) { @@ -32,12 +45,22 @@ namespace Serein.Library return dict; } + /// + /// 数组组装,将入参的值组装成一个数组对象 + /// + /// + /// [NodeAction(NodeType.Action, "数组组装")] public static object[] ArraySet(params object[] value) { return value; } + /// + /// 输出到控制台,使用SereinEnv.WriteLine方法输出信息 + /// + /// + /// [NodeAction(NodeType.Action, "输出")] public static object[] Console(params object[] value) { @@ -48,7 +71,14 @@ namespace Serein.Library return value; } - [NodeAction(NodeType.Action, "逻辑分支")] + /// + /// 逻辑分支,根据布尔值选择返回的值,如果布尔值为true则返回t_value,否则返回f_value + /// + /// + /// + /// + /// + [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; } + /// + /// 文本拼接,将多个文本值拼接成一个字符串,支持换行符和制表符的特殊处理 + /// + /// + /// + [NodeAction(NodeType.Action, "文本拼接")] public static string TextJoin(params object[] value) { @@ -79,6 +115,13 @@ namespace Serein.Library return sb.ToString(); } + /// + /// 动态构建对象,将字典中的键值对转换为一个动态对象,支持指定类名和打印结果 + /// + /// + /// + /// + /// [NodeAction(NodeType.Action, "键值对动态构建对象")] public static object CreateDynamicObjectOfDict(Dictionary 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; - } - } } diff --git a/Library/FlowNode/DelegateDetails.cs b/Library/FlowNode/DelegateDetails.cs index a352218..eb34fbe 100644 --- a/Library/FlowNode/DelegateDetails.cs +++ b/Library/FlowNode/DelegateDetails.cs @@ -57,9 +57,18 @@ namespace Serein.Library CollectionSetter } + /// + /// 表示方法的类型 + /// public enum GSType - { + { + /// + /// 获取值 + /// Get, + /// + /// 设置值 + /// Set, } @@ -149,7 +158,7 @@ namespace Serein.Library /// 目前提供了创建集合取值/赋值委托 /// /// 类型信息 - /// 操作类型 + /// 操作类型 public DelegateDetails(Type type, EmitType emitType) { if (emitType == EmitType.CollectionSetter) diff --git a/Library/FlowNode/FlipflopContext.cs b/Library/FlowNode/FlipflopContext.cs index 885b86f..24d4be7 100644 --- a/Library/FlowNode/FlipflopContext.cs +++ b/Library/FlowNode/FlipflopContext.cs @@ -5,6 +5,9 @@ using System.Threading.Tasks; namespace Serein.Library { + /// + /// FlipflopFunc 类提供了与 Flipflop 相关的功能方法。 + /// public static class FlipflopFunc { /// @@ -72,15 +75,34 @@ namespace Serein.Library /// public class FlipflopContext : IFlipflopContext { + /// + /// 触发器完成的状态(根据业务场景手动设置) + /// public FlipflopStateType State { get; set; } + /// + /// 触发类型 + /// public TriggerDescription Type { get; set; } + /// + /// 触发时传递的数据 + /// public TResult Value { get; set; } + /// + /// 触发器上下文构造函数 + /// + /// public FlipflopContext(FlipflopStateType ffState) { State = ffState; } + + /// + /// 触发器上下文构造函数,传入状态和数据值 + /// + /// + /// public FlipflopContext(FlipflopStateType ffState, TResult value) { State = ffState; diff --git a/Library/FlowNode/FlowCanvasDetails.cs b/Library/FlowNode/FlowCanvasDetails.cs index ae43fc9..c2f4d54 100644 --- a/Library/FlowNode/FlowCanvasDetails.cs +++ b/Library/FlowNode/FlowCanvasDetails.cs @@ -16,81 +16,88 @@ namespace Serein.Library /// /// 流程画布 /// - [NodeProperty(ValuePath = NodeValuePath.Node)] + [FlowDataProperty(ValuePath = NodeValuePath.Node)] public partial class FlowCanvasDetails { + /// + /// 流程画布的构造函数 + /// + /// public FlowCanvasDetails(IFlowEnvironment env) { Env = env; } + /// + /// 流程画布的运行环境 + /// public IFlowEnvironment Env { get; } /// /// 画布拥有的节点 /// - [PropertyInfo(IsProtection = false)] + [DataInfo(IsProtection = false)] private List _nodes = []; //private System.Collections.ObjectModel.ObservableCollection _nodes = []; /// /// 画布公开的节点 /// - [PropertyInfo(IsProtection = false)] + [DataInfo(IsProtection = false)] private List _publicNodes = []; /// /// 标识画布ID /// - [PropertyInfo(IsProtection = false)] + [DataInfo(IsProtection = false)] private string _guid; /// /// 画布名称 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string _name; /// /// 画布宽度 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private double _width; /// /// 画布高度 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private double _height; /// /// 预览位置X /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private double _viewX; /// /// 预览位置Y /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private double _viewY; /// /// 缩放比例X /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private double _scaleX = 1; /// /// 缩放比例Y /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private double _scaleY = 1; /// /// 起始节点 /// - [PropertyInfo] + [DataInfo] private IFlowNode _startNode; } diff --git a/Library/FlowNode/FlowContext.cs b/Library/FlowNode/FlowContext.cs index 27d599d..e11ebc4 100644 --- a/Library/FlowNode/FlowContext.cs +++ b/Library/FlowNode/FlowContext.cs @@ -21,7 +21,6 @@ namespace Serein.Library /// 动态流程上下文 /// /// 脚本运行时的IOC - /// 脚本运行时使用的IOC容器 public FlowContext(IFlowEnvironment flowEnvironment) { Env = flowEnvironment; @@ -110,6 +109,10 @@ namespace Serein.Library return flowInvokeInfo; } + /// + /// 获取当前流程上下文的所有节点调用信息,包含每个节点的执行时间、调用类型、执行状态等。 + /// + /// public List GetAllInvokeInfos() => [.. flowInvokeInfos.Values]; /// diff --git a/Library/FlowNode/FlowResult.cs b/Library/FlowNode/FlowResult.cs index cd6d4ba..f80163c 100644 --- a/Library/FlowNode/FlowResult.cs +++ b/Library/FlowNode/FlowResult.cs @@ -9,17 +9,6 @@ using System.Threading.Tasks; namespace Serein.Library { - /// - /// 表示空数据 - /// - /*public readonly struct Unit : IEquatable - { - 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; - }*/ - /// /// 流程返回值的包装 diff --git a/Library/FlowNode/JunctionModel.cs b/Library/FlowNode/JunctionModel.cs index 2abede1..d1bfd76 100644 --- a/Library/FlowNode/JunctionModel.cs +++ b/Library/FlowNode/JunctionModel.cs @@ -33,6 +33,11 @@ namespace Serein.Library.FlowNode /// public class JunctionModel { + /// + /// 连接点模型构造函数 + /// + /// + /// public JunctionModel(IFlowNode NodeModel, JunctionType JunctionType) { Guid = System.Guid.NewGuid().ToString(); diff --git a/Library/FlowNode/LightweightFlowEnvironment.cs b/Library/FlowNode/LightweightFlowEnvironment.cs index 21b509d..de8c408 100644 --- a/Library/FlowNode/LightweightFlowEnvironment.cs +++ b/Library/FlowNode/LightweightFlowEnvironment.cs @@ -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; - } - } - - -*/ + /// + /// 流程调用树,管理所有的调用节点 + /// public class FlowCallTree : IFlowCallTree { private readonly SortedDictionary _callNodes = new SortedDictionary(); //private readonly Dictionary _callNodes = new Dictionary(); + /// + /// 索引器,允许通过字符串索引访问CallNode + /// + /// + /// public CallNode this[string index] { get @@ -92,17 +36,33 @@ namespace Serein.Library } } + /// + /// 添加一个调用节点到流程调用树中 + /// + /// + /// public void AddCallNode(string nodeGuid, Action action) { var node = new CallNode(nodeGuid, action); _callNodes[nodeGuid] = node; } + + /// + /// 添加一个调用节点到流程调用树中,使用异步函数 + /// + /// + /// public void AddCallNode(string nodeGuid, Func func) { var node = new CallNode(nodeGuid, func); _callNodes[nodeGuid] = node; } + /// + /// 获取指定Key的CallNode,如果不存在则返回null + /// + /// + /// public CallNode Get(string key) { return _callNodes.TryGetValue(key, out CallNode callNode) ? callNode : null; @@ -112,7 +72,9 @@ namespace Serein.Library - + /// + /// 调用节点,代表一个流程中的调用点,可以是一个Action或一个异步函数。 + /// public class CallNode { @@ -120,11 +82,21 @@ namespace Serein.Library private Func taskFunc; private Action action; + /// + /// 创建一个新的调用节点,使用指定的节点Guid。 + /// + /// public CallNode(string nodeGuid) { Guid = nodeGuid; Init(); } + + /// + /// 创建一个新的调用节点,使用指定的节点Guid和Action。 + /// + /// + /// public CallNode(string nodeGuid, Action action) { Guid = nodeGuid; @@ -132,6 +104,11 @@ namespace Serein.Library Init(); } + /// + /// 创建一个新的调用节点,使用指定的节点Guid和异步函数。 + /// + /// + /// public CallNode(string nodeGuid, Func func) { Guid = nodeGuid; @@ -139,7 +116,9 @@ namespace Serein.Library Init(); } - + /// + /// 初始化调用节点,设置默认的子节点和后继节点字典。 + /// private void Init() { //PreviousNodes = new Dictionary>(); @@ -151,17 +130,28 @@ namespace Serein.Library } } + private enum ActionType { Action, Task, } private ActionType actionType = ActionType.Action; + + /// + /// 设置调用节点的Action,表示该节点执行一个同步操作。 + /// + /// public void SetAction(Action action) { this.action = action; actionType = ActionType.Action; } + + /// + /// 设置调用节点的异步函数,表示该节点执行一个异步操作。 + /// + /// public void SetAction(Func taskFunc) { this.taskFunc = taskFunc; @@ -173,15 +163,23 @@ namespace Serein.Library /// 对应的节点 /// public string Guid { get; } + +#if false + /// /// 不同分支的父节点(流程调用) /// - //public Dictionary> PreviousNodes { get; private set; } + public Dictionary> PreviousNodes { get; private set; } +#endif /// /// 不同分支的子节点(流程调用) /// public Dictionary> SuccessorNodes { get; private set; } + + /// + /// 子节点数组,分为四个分支:上游、成功、失败、错误,每个分支最多支持16个子节点。 + /// public CallNode[][] ChildNodes { get; private set; } = new CallNode[][] { new CallNode[MaxChildNodeCount], @@ -191,7 +189,12 @@ namespace Serein.Library }; private const int MaxChildNodeCount = 16; // 每个分支最多支持16个子节点 - + + /// + /// 获取指定类型的子节点数量。 + /// + /// + /// public int GetCount(ConnectionInvokeType type) { if (type == ConnectionInvokeType.Upstream) return UpstreamNodeCount; @@ -201,11 +204,29 @@ namespace Serein.Library return 0; } + /// + /// 获取当前节点的子节点数量。 + /// public int UpstreamNodeCount { get; private set; } = 0; + + /// + /// 获取当前节点的成功后继子节点数量。 + /// public int IsSuccessorNodeCount { get; private set; } = 0; + /// + /// 获取当前节点的失败后继子节点数量。 + /// public int IsFailNodeCount { get; private set; } = 0; + /// + /// 获取当前节点的错误后继子节点数量。 + /// public int IsErrorNodeCount { get; private set; } = 0; + /// + /// 添加一个上游子节点到当前节点。 + /// + /// + /// public CallNode AddChildNodeUpstream(CallNode callNode) { var connectionInvokeType = ConnectionInvokeType.Upstream; @@ -214,6 +235,11 @@ namespace Serein.Library return this; } + /// + /// 添加一个成功后继子节点到当前节点。 + /// + /// + /// public CallNode AddChildNodeSucceed(CallNode callNode) { ChildNodes[0][UpstreamNodeCount++] = callNode; @@ -224,6 +250,11 @@ namespace Serein.Library return this; } + /// + /// 添加一个失败后继子节点到当前节点。 + /// + /// + /// public CallNode AddChildNodeFail(CallNode callNode) { var connectionInvokeType = ConnectionInvokeType.IsFail; @@ -232,6 +263,12 @@ namespace Serein.Library return this; } + + /// + /// 添加一个错误后继子节点到当前节点。 + /// + /// + /// public CallNode AddChildNodeError(CallNode callNode) { var connectionInvokeType = ConnectionInvokeType.IsError; @@ -268,7 +305,7 @@ namespace Serein.Library } } - private static readonly DefaultObjectPool> _stackPool = new DefaultObjectPool>(new DefaultPooledObjectPolicy>()); + private static readonly ObjectPool> _stackPool = new ObjectPool>(() => new Stack()); /// @@ -279,7 +316,7 @@ namespace Serein.Library /// public async Task 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 } + /// + /// 流程调用树接口,提供获取CallNode的方法。 + /// public interface IFlowCallTree { + /// + /// 获取指定Key的CallNode,如果不存在则返回null。 + /// + /// + /// CallNode Get(string key); } @@ -371,10 +416,22 @@ namespace Serein.Library { private readonly IFlowCallTree flowCallTree; private readonly IFlowEnvironment flowEnvironment; + + /// + /// 轻量级流程上下文池,使用对象池模式来管理流程上下文的创建和回收。 + /// public static Serein.Library.Utils.ObjectPool FlowContextPool { get; set; } + /// + /// 单例IOC容器,用于依赖注入和服务定位。 + /// public ISereinIOC IOC => throw new NotImplementedException(); + /// + /// 轻量级流程控制器构造函数,接受流程调用树和流程环境作为参数。 + /// + /// + /// public LightweightFlowControl(IFlowCallTree flowCallTree, IFlowEnvironment flowEnvironment) { this.flowCallTree = flowCallTree; @@ -385,11 +442,12 @@ namespace Serein.Library }); } + /// public Task InvokeAsync(string apiGuid, Dictionary dict) { throw new NotImplementedException(); } - + /// public Task InvokeAsync(string apiGuid, Dictionary dict) { throw new NotImplementedException(); @@ -398,7 +456,7 @@ namespace Serein.Library //private readonly DefaultObjectPool _stackPool = new DefaultObjectPool(new DynamicContext(this)); - + /// public async Task StartFlowAsync(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 StartFlowAsync(string[] canvasGuids) + + /// + public Task StartFlowAsync(string[] canvasGuids) { throw new NotImplementedException(); } + /// public Task ExitFlowAsync() { throw new NotImplementedException(); } #region 无须实现 - + /// public void ActivateFlipflopNode(string nodeGuid) { throw new NotImplementedException(); } - + /// public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) { throw new NotImplementedException(); } - + /// public void TerminateFlipflopNode(string nodeGuid) { throw new NotImplementedException(); } - + /// public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) { throw new NotImplementedException(); } - + /// public void UseExternalIOC(ISereinIOC ioc) { throw new NotImplementedException(); } - + /// public void UseExternalIOC(ISereinIOC ioc, Action setDefultMemberOnReset = null) { throw new NotImplementedException(); @@ -495,110 +556,128 @@ namespace Serein.Library /// public class LightweightFlowEnvironmentEvent : 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; - + /// public void OnDllLoad(LoadDllEventArgs eventArgs) { DllLoad?.Invoke(eventArgs); } - + /// public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs) { ProjectLoaded?.Invoke(eventArgs); } - + /// public void OnProjectSaving(ProjectSavingEventArgs eventArgs) { ProjectSaving?.Invoke(eventArgs); } - + /// public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs) { NodeConnectChanged?.Invoke(eventArgs); } - + /// public void OnCanvasCreated(CanvasCreateEventArgs eventArgs) { CanvasCreated?.Invoke(eventArgs); } - + /// public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs) { CanvasRemoved?.Invoke(eventArgs); } - + /// public void OnNodeCreated(NodeCreateEventArgs eventArgs) { NodeCreated?.Invoke(eventArgs); } - + /// public void OnNodeRemoved(NodeRemoveEventArgs eventArgs) { NodeRemoved?.Invoke(eventArgs); } - + /// public void OnNodePlace(NodePlaceEventArgs eventArgs) { NodePlace?.Invoke(eventArgs); } - + /// public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs) { NodeTakeOut?.Invoke(eventArgs); } - + /// public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs) { StartNodeChanged?.Invoke(eventArgs); } - + /// public void OnFlowRunComplete(FlowEventArgs eventArgs) { FlowRunComplete?.Invoke(eventArgs); } - + /// public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs) { MonitorObjectChanged?.Invoke(eventArgs); } - + /// public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs) { NodeInterruptStateChanged?.Invoke(eventArgs); } - + /// public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs) { InterruptTriggered?.Invoke(eventArgs); } - + /// public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs) { IOCMembersChanged?.Invoke(eventArgs); } - + /// public void OnNodeLocated(NodeLocatedEventArgs eventArgs) { NodeLocated?.Invoke(eventArgs); } - + /// public void OnEnvOutput(InfoType type, string value) { EnvOutput?.Invoke(type, value); @@ -611,128 +690,131 @@ namespace Serein.Library /// public class LightweightFlowEnvironment : IFlowEnvironment { - + /// + /// 轻量级流程环境构造函数,接受一个流程环境事件接口。 + /// + /// public LightweightFlowEnvironment(IFlowEnvironmentEvent lightweightFlowEnvironmentEvent) { this.Event = lightweightFlowEnvironmentEvent; } - - + /// public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial) { Console.WriteLine(message); } - + /// public ISereinIOC IOC => throw new NotImplementedException(); - + /// public IFlowEdit FlowEdit => throw new NotImplementedException(); - + /// public IFlowControl FlowControl => throw new NotImplementedException(); - + /// public IFlowEnvironmentEvent Event { get; private set; } - + /// public string EnvName => throw new NotImplementedException(); - + /// public string ProjectFileLocation => throw new NotImplementedException(); - + /// public bool _IsGlobalInterrupt => throw new NotImplementedException(); - + /// public bool IsControlRemoteEnv => throw new NotImplementedException(); - + /// public InfoClass InfoClass { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + /// public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - + /// public IFlowEnvironment CurrentEnv => throw new NotImplementedException(); - + /// 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(); + }*/ + /// public void ExitRemoteEnv() { throw new NotImplementedException(); } - + /// public Task GetEnvInfoAsync() { throw new NotImplementedException(); } - - public Task GetProjectInfoAsync() + /// + public SereinProjectData GetProjectInfoAsync() { throw new NotImplementedException(); } - + /// public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) { throw new NotImplementedException(); } - + /// public void LoadLibrary(string dllPath) { throw new NotImplementedException(); } - + /// public bool LoadNativeLibraryOfRuning(string file) { throw new NotImplementedException(); } - + /// public void LoadProject(string filePath) { throw new NotImplementedException(); } - + /// public Task LoadProjetAsync(string filePath) { throw new NotImplementedException(); } - + /// public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) { throw new NotImplementedException(); } - + /// public void SaveProject() { throw new NotImplementedException(); } - + /// public void SetUIContextOperation(UIContextOperation uiContextOperation) { throw new NotImplementedException(); } - + /// public Task StartRemoteServerAsync(int port = 7525) { throw new NotImplementedException(); } - + /// public void StopRemoteServer() { throw new NotImplementedException(); } - + /// public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del) { throw new NotImplementedException(); } - + /// public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo) { throw new NotImplementedException(); } - + /// public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel) { throw new NotImplementedException(); } - + /// public bool TryUnloadLibrary(string assemblyFullName) { throw new NotImplementedException(); diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs index 5a5b532..ad3021c 100644 --- a/Library/FlowNode/MethodDetails.cs +++ b/Library/FlowNode/MethodDetails.cs @@ -12,7 +12,7 @@ namespace Serein.Library /// /// 每个节点有独自的MethodDetails实例 /// - [NodeProperty(ValuePath = NodeValuePath.Method)] + [FlowDataProperty(ValuePath = NodeValuePath.Method)] public partial class MethodDetails { // private readonly IFlowEnvironment env; @@ -20,57 +20,51 @@ namespace Serein.Library /// /// 对应的节点 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private IFlowNode _nodeModel; /// /// 对应的程序集 /// - [PropertyInfo] + [DataInfo] private string _assemblyName; /// /// 调用节点方法时需要的实例(多个相同的节点将拥有相同的类型) /// - [PropertyInfo] + [DataInfo] private Type _actingInstanceType; - /// - /// 作用实例(多个相同的节点将会共享同一个实例) - /// - // [PropertyInfo] - // private object _actingInstance; - /// /// 方法名称 /// - [PropertyInfo] + [DataInfo] private string _methodName; /// /// 节点类型 /// - [PropertyInfo] + [DataInfo] private NodeType _methodDynamicType; /// /// 锁名称(暂未实现) /// - [PropertyInfo] + [DataInfo] private string _methodLockName; /// /// 方法别名 /// - [PropertyInfo] + [DataInfo] private string _methodAnotherName; /// /// 参数描述 /// - [PropertyInfo] + [DataInfo] private ParameterDetails[] _parameterDetailss; /// @@ -78,19 +72,19 @@ namespace Serein.Library /// -1表示不存在 /// 0表示第一个参数是可选参数 /// - [PropertyInfo] + [DataInfo] private int _paramsArgIndex = -1; /// /// 是否为异步方法(如果为异步方法,则返回值类型为Task或Task<T>) /// - [PropertyInfo] + [DataInfo] private bool _isAsync = false; /// /// 出参类型 /// - [PropertyInfo] + [DataInfo] private Type _returnType; } @@ -133,7 +127,11 @@ namespace Serein.Library return true; } - + /// + /// 新增可变参数 + /// + /// + /// public bool AddParamsArg(ParameterDetails parameterDetails) { if (ParamsArgIndex < 0) // 方法是否包含可变参数 @@ -313,6 +311,10 @@ namespace Serein.Library return md; } + /// + /// 将方法信息转换为字符串,方便调试和查看 + /// + /// public override string ToString() { if (string.IsNullOrEmpty(this.MethodName)) diff --git a/Library/FlowNode/NodeDebugSetting.cs b/Library/FlowNode/NodeDebugSetting.cs index c7d5027..51706a3 100644 --- a/Library/FlowNode/NodeDebugSetting.cs +++ b/Library/FlowNode/NodeDebugSetting.cs @@ -8,7 +8,7 @@ namespace Serein.Library /// /// 节点调试设置,用于中断节点的运行 /// - [NodeProperty(ValuePath = NodeValuePath.DebugSetting)] + [FlowDataProperty(ValuePath = NodeValuePath.DebugSetting)] public partial class NodeDebugSetting { /// @@ -24,25 +24,25 @@ namespace Serein.Library /// /// 是否保护参数 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private bool _isProtectionParameter = false; /// /// 对应的节点 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private IFlowNode _nodeModel; /// /// 是否使能 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private bool _isEnable = true; /// /// 是否中断节点。 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private bool _isInterrupt = false; } diff --git a/Library/FlowNode/NodeMVVMService.cs b/Library/FlowNode/NodeMVVMService.cs index a40d1f7..72906fa 100644 --- a/Library/FlowNode/NodeMVVMService.cs +++ b/Library/FlowNode/NodeMVVMService.cs @@ -36,6 +36,11 @@ namespace Serein.Library /// public Type ViewModelType { get; set; } + /// + /// 节点类型信息字符串表示 + /// + /// + public override string ToString() { return $"$[{NodeType}]类型信息 : ModelType->{ModelType};ControlType->{ControlType};ViewModelType->{ViewModelType}"; diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 37f0765..b8c637b 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -14,7 +14,7 @@ namespace Serein.Library /// /// 节点入参参数详情 /// - [NodeProperty(ValuePath = NodeValuePath.Parameter)] + [FlowDataProperty(ValuePath = NodeValuePath.Parameter)] public partial class ParameterDetails { // private readonly IFlowEnvironment env; @@ -22,13 +22,13 @@ namespace Serein.Library /// /// 所在的节点 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private IFlowNode _nodeModel; /// /// 参数索引 /// - [PropertyInfo] + [DataInfo] private int _index; /// @@ -36,7 +36,7 @@ namespace Serein.Library /// 如果为 true ,则使用输入的文本值作为入参数据。 /// 如果为 false ,则在当前流程上下文中,根据 ArgDataSourceNodeGuid 查找到对应节点,并根据 ArgDataSourceNodeGuid 判断如何获取其返回的数据,以此作为入参数据。 /// - [PropertyInfo(IsNotification = true, IsVerify = true)] + [DataInfo(IsNotification = true, IsVerify = true)] private bool _isExplicitData ; ///// @@ -49,7 +49,7 @@ namespace Serein.Library /// 方法入参若无相关转换器特性标注,则无需关注该变量。该变量用于需要用到枚举BinValue转换器时,指示相应的入参变量需要转为的类型。 /// [Obsolete("转换器特性将在下一个大版本中移除")] - [PropertyInfo] + [DataInfo] private Type _explicitType ; /// @@ -58,56 +58,56 @@ namespace Serein.Library /// Bool : 布尔类型 /// Value :除以上类型之外的任意参数 /// - [PropertyInfo] + [DataInfo] private ParameterValueInputType _inputType ; /// /// 入参数据来源。默认使用上一节点作为入参数据。 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private ConnectionArgSourceType _argDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; /// /// 当 ArgDataSourceType 不为 GetPreviousNodeData 时(从运行时上一节点获取数据)。 /// 则通过当前上下文,获取该Guid对应的数据作为预处理的入参参数。 /// - [PropertyInfo] + [DataInfo] private string _argDataSourceNodeGuid; /// /// 方法入参需要的类型。 /// - [PropertyInfo] + [DataInfo] private Type _dataType ; /// /// 方法入参参数名称 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string _name ; /// /// 入参注释 /// - [PropertyInfo] + [DataInfo] private string _description; /// /// 自定义的方法入参数据 /// - [PropertyInfo(IsNotification = true)] // IsPrint = true + [DataInfo(IsNotification = true)] // IsPrint = true private string _dataValue; /// /// 只有当 InputType 为 Select 时,才会需要该成员。 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string[] _items ; /// /// 指示该属性是可变参数的其中一员(可变参数为数组类型) /// - [PropertyInfo] + [DataInfo] private bool _isParams; } @@ -134,6 +134,11 @@ namespace Serein.Library this.NodeModel = nodeModel; } + /// + /// 通过参数数据加载实体,用于加载项目文件、远程连接的场景 + /// + /// + /// 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; } + /// + /// 转为方法入参数据 + /// + /// + /// + /// public async Task ToMethodArgData(IFlowContext context) { // 1. 从缓存获取 @@ -310,177 +322,181 @@ namespace Serein.Library - /* /// - /// 转为方法入参数据 + /* /// + /// 转为方法入参数据 + /// + /// + public async Task 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 async Task 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}"; diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs index 163fc8e..13c1c26 100644 --- a/Library/FlowNode/SereinProjectData.cs +++ b/Library/FlowNode/SereinProjectData.cs @@ -270,7 +270,7 @@ namespace Serein.Library /// /// 节点于画布中的位置(通用类) /// - [NodeProperty] + [FlowDataProperty] public partial class PositionOfUI { /// @@ -285,13 +285,13 @@ namespace Serein.Library /// /// 指示控件在画布的横向向方向上的位置 /// - [PropertyInfo] + [DataInfo] private double _x = 0; /// /// 指示控件在画布的纵向方向上的位置 /// - [PropertyInfo] + [DataInfo] private double _y = 0; } diff --git a/Library/Properties/launchSettings.json b/Library/Properties/launchSettings.json new file mode 100644 index 0000000..2bf9c54 --- /dev/null +++ b/Library/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "Serein.Library": { + "commandName": "Project", + "remoteDebugEnabled": false + } + } +} \ No newline at end of file diff --git a/Library/ScriptBaseFunc.cs b/Library/ScriptBaseFunc.cs index 120e855..5e83303 100644 --- a/Library/ScriptBaseFunc.cs +++ b/Library/ScriptBaseFunc.cs @@ -8,35 +8,83 @@ namespace Serein.Library { + /// + /// 脚本代码中常用的函数 + /// public static class ScriptBaseFunc { + /// + /// 获取当前时间 + /// + /// public static DateTime now() => DateTime.Now; #region 常用的类型转换 + /// + /// 将值转换为bool类型 + /// + /// + /// public static bool @bool(object value) { return ConvertHelper.ValueParse(value); } + + /// + /// 将值转换为字节类型 + /// + /// + /// public static byte @byte(object value) { return ConvertHelper.ValueParse(value); } + + /// + /// 将值转换为短整型 + /// + /// + /// public static decimal @decimal(object value) { return ConvertHelper.ValueParse(value); } + + /// + /// 将值转换为浮点型 + /// + /// + /// public static float @float(object value) { return ConvertHelper.ValueParse(value); } + + /// + /// 将值转换为双精度浮点型 + /// + /// + /// public static double @double(object value) { return ConvertHelper.ValueParse(value); } + + /// + /// 将值转换为整数类型 + /// + /// + /// public static int @int(object value) { return ConvertHelper.ValueParse(value); } + + /// + /// 将值转换为长整型 + /// + /// + /// public static int @long(object value) { return ConvertHelper.ValueParse(value); @@ -44,6 +92,12 @@ namespace Serein.Library #endregion + /// + /// 获取集合或数组的长度 + /// + /// + /// + /// public static int len(object target) { // 获取数组或集合对象 @@ -70,28 +124,50 @@ namespace Serein.Library } } - + /// + /// 将对象转换为字符串 + /// + /// + /// public static string str(object obj) { return obj?.ToString() ?? string.Empty; } - + /// + /// 获取全局数据 + /// + /// + /// public static object global(string name) { return SereinEnv.GetFlowGlobalData(name); } + /// + /// 获取对象的类型 + /// + /// + /// public static Type type(object type) { return type.GetType(); } + /// + /// 记录日志信息 + /// + /// public static void log(object value) { SereinEnv.WriteLine(InfoType.INFO, value?.ToString()); } + /// + /// 等待一段时间 + /// + /// + /// public static async Task sleep(object value) { if (value is int @int) diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index 7e5539e..3942ca7 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -2,7 +2,7 @@ SereinFow - 1.2.2 + 1.2.3 动态节点流、可视化编辑的基本依赖,支持导入C# DLL生成自定义节点,提供二次开发支持,适合用于可视化编程和流程设计 README.md https://github.com/fhhyyp/serein-flow @@ -61,18 +61,13 @@ - + - - - - - @@ -87,6 +82,8 @@ + + diff --git a/Library/Utils/ArrayHelper.cs b/Library/Utils/ArrayHelper.cs index 90175c5..6dc67c6 100644 --- a/Library/Utils/ArrayHelper.cs +++ b/Library/Utils/ArrayHelper.cs @@ -6,6 +6,9 @@ using System.Threading.Tasks; namespace Serein.Library.Utils { + /// + /// 数组操作的工具类 + /// public class ArrayHelper { diff --git a/Library/Utils/ConvertHelper.cs b/Library/Utils/ConvertHelper.cs index 090f79d..4e4a13b 100644 --- a/Library/Utils/ConvertHelper.cs +++ b/Library/Utils/ConvertHelper.cs @@ -270,6 +270,14 @@ namespace Serein.Library.Utils return (T)result; } + + /// + /// 将字符串转换为指定类型的值对象。 + /// + /// + /// + /// + /// public static object ToValueData(this string valueStr, Type type) { if (string.IsNullOrWhiteSpace(valueStr)) diff --git a/Library/Utils/DynamicObjectHelper.cs b/Library/Utils/DynamicObjectHelper.cs index 7d8d8ee..b8d64df 100644 --- a/Library/Utils/DynamicObjectHelper.cs +++ b/Library/Utils/DynamicObjectHelper.cs @@ -41,21 +41,40 @@ namespace Serein.Library.Utils return null; } + /// + /// 解析字典属性并创建对象实例 + /// + /// + /// + /// + public static object Resolve(IDictionary properties, string typeName) { var obj = CreateObjectWithProperties(properties, typeName); //SetPropertyValues(obj, properties); return obj; } + + /// + /// 尝试解析字典属性并创建对象实例 + /// + /// + /// + /// + /// public static bool TryResolve(IDictionary properties, string typeName, out object result) { result = CreateObjectWithProperties(properties, typeName); bool success = SetPropertyValuesWithValidation(result, properties); return success; - // 打印赋值结果 } - // 递归方法:打印对象属性及类型 + + /// + /// 打印对象属性及类型 + /// + /// + /// public static void PrintObjectProperties(object obj, string indent = "") { var objType = obj.GetType(); @@ -327,7 +346,12 @@ namespace Serein.Library.Utils } #region 动态创建对象并赋值 - // 方法 1: 创建动态类型及其对象实例 + /// + /// 创建动态类型及其对象实例 + /// + /// + /// + /// public static object CreateObjectWithProperties(IDictionary properties, string typeName) { // 如果类型已经缓存,直接返回缓存的类型 @@ -410,8 +434,12 @@ namespace Serein.Library.Utils return Activator.CreateInstance(dynamicType); } - // 方法 2: 递归设置对象的属性值 - public static void SetPropertyValues(object obj, Dictionary properties) + /// + /// 递归设置对象的属性值 + /// + /// + /// + private static void SetPropertyValues(object obj, Dictionary properties) { var objType = obj.GetType(); @@ -439,8 +467,13 @@ namespace Serein.Library.Utils } } } - // 方法 2: 递归设置对象的属性值(带验证) + /// + /// 递归设置对象的属性值(带验证) + /// + /// + /// + /// public static bool SetPropertyValuesWithValidation(object obj, IDictionary properties) { var objType = obj.GetType(); diff --git a/Library/Utils/EmitHelper.cs b/Library/Utils/EmitHelper.cs index 28666f1..870f9d9 100644 --- a/Library/Utils/EmitHelper.cs +++ b/Library/Utils/EmitHelper.cs @@ -9,12 +9,15 @@ using System.Threading.Tasks; namespace Serein.Library.Utils { + /// /// Emit创建委托工具类 /// public class EmitHelper { - + /// + /// 动态方法信息 + /// public class EmitMethodInfo { /// @@ -39,6 +42,9 @@ namespace Serein.Library.Utils } + /// + /// 方法类型枚举 + /// public enum EmitMethodType { /// @@ -55,12 +61,19 @@ namespace Serein.Library.Utils TaskHasResult, } - public static bool IsGenericTask(Type returnType, out Type taskResult) + /// + /// 判断一个类型是否为泛型 Task<T> 或 Task,并返回泛型参数类型(如果有的话) + /// + /// + /// + /// +#nullable enable + public static bool IsGenericTask(Type returnType, out Type? taskResult) { // 判断是否为 Task 类型或泛型 Task 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 } } - /// /// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托 /// @@ -87,6 +99,10 @@ namespace Serein.Library.Utils /// 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。"); diff --git a/Library/Utils/EnumHelper.cs b/Library/Utils/EnumHelper.cs index 470706a..f8b5ad0 100644 --- a/Library/Utils/EnumHelper.cs +++ b/Library/Utils/EnumHelper.cs @@ -59,6 +59,13 @@ namespace Serein.Library.Utils return attribute != null ? (TResult)valueSelector(attribute) : default; } + /// + /// 从枚举值的 BindValueAttribute 特性中 获取绑定的参数(用于绑定了某些内容的枚举值) + /// + /// + /// + /// + /// public static object GetBoundValue(Type enumType,object enumValue, Func valueSelector) { var fieldInfo = enumType.GetField(enumValue.ToString()); diff --git a/Library/Utils/ExpressionHelper.cs b/Library/Utils/ExpressionHelper.cs index 27186b5..8019bb7 100644 --- a/Library/Utils/ExpressionHelper.cs +++ b/Library/Utils/ExpressionHelper.cs @@ -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 /// - /// 表达式树构建无参数,有返回值(Task)的方法(触发器) + /// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器) /// public static Delegate MethodCallerAsync(Type type, MethodInfo methodInfo) { @@ -314,7 +314,7 @@ namespace Serein.Library.Utils } /// - /// 表达式树构建无参数,有返回值(Task)的方法(触发器) + /// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器) /// /// /// @@ -332,7 +332,7 @@ namespace Serein.Library.Utils /// - /// 表达式树构建多个参数,有返回值(Task-object)的方法(触发器) + /// 表达式树构建多个参数,有返回值(Task<object>)的方法(触发器) /// public static Delegate MethodCallerAsync(Type type, MethodInfo method, params Type[] parameterTypes) { @@ -342,7 +342,7 @@ namespace Serein.Library.Utils } /// - /// 表达式树构建多个参数,有返回值(Task)的方法(触发器) + /// 表达式树构建多个参数,有返回值(Task<object>)的方法(触发器) /// 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 - 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; - - } - } - + /// + /// / 自动创建一个委托,根据方法信息和类型 + /// + /// + /// + /// public static Delegate AutoCreate(Type type, MethodInfo methodInfo) { Type returnType = methodInfo.ReturnType; diff --git a/Library/Utils/FlowTrigger/ChannelFlowTrigger.cs b/Library/Utils/FlowTrigger/ChannelFlowTrigger.cs index c7dc07d..9e31180 100644 --- a/Library/Utils/FlowTrigger/ChannelFlowTrigger.cs +++ b/Library/Utils/FlowTrigger/ChannelFlowTrigger.cs @@ -9,6 +9,10 @@ using System.Threading.Tasks; namespace Serein.Library.Utils { + /// + /// 基于 Channel 的触发器实现 + /// + /// public class ChannelFlowTrigger : IFlowTrigger { // 使用并发字典管理每个枚举信号对应的 Channel @@ -24,6 +28,13 @@ namespace Serein.Library.Utils return _channels.GetOrAdd(signal, _ => Channel.CreateUnbounded>()); } + /// + /// 等待信号触发并指定超时时间 + /// + /// + /// + /// + /// public async Task> WaitTriggerWithTimeoutAsync(TSignal signal, TimeSpan outTime) { var channel = GetOrCreateChannel(signal); @@ -54,6 +65,12 @@ namespace Serein.Library.Utils } + /// + /// 等待信号触发 + /// + /// + /// + /// public async Task> WaitTriggerAsync(TSignal signal) { var channel = GetOrCreateChannel(signal); @@ -76,6 +93,13 @@ namespace Serein.Library.Utils } } + /// + /// 调用触发器 + /// + /// + /// + /// + /// public async Task InvokeTriggerAsync(TSignal signal, TResult value) { if (_channels.TryGetValue(signal, out var channel)) @@ -92,6 +116,9 @@ namespace Serein.Library.Utils return false; } + /// + /// 取消所有触发器 + /// public void CancelAllTrigger() { foreach (var channel in _channels.Values) diff --git a/Library/Utils/FlowTrigger/TaskFlowTrigger.cs b/Library/Utils/FlowTrigger/TaskFlowTrigger.cs index 4822dec..a46e328 100644 --- a/Library/Utils/FlowTrigger/TaskFlowTrigger.cs +++ b/Library/Utils/FlowTrigger/TaskFlowTrigger.cs @@ -141,13 +141,30 @@ namespace Serein.Library.Utils { private readonly Action _onNext; + /// + /// 构造函数,接受一个 Action 作为回调 + /// + /// public Observer(Action onNext) { _onNext = onNext; } + /// + /// 通知订阅者已完成或发生错误 + /// public void OnCompleted() { } + + /// + /// 通知订阅者发生错误 + /// + /// public void OnError(Exception error) { } + + /// + /// 通知订阅者有新数据到来 + /// + /// public void OnNext(T value) { _onNext?.Invoke(value); diff --git a/Library/Utils/FlowTrigger/TriggerResult.cs b/Library/Utils/FlowTrigger/TriggerResult.cs index 728d105..1a58934 100644 --- a/Library/Utils/FlowTrigger/TriggerResult.cs +++ b/Library/Utils/FlowTrigger/TriggerResult.cs @@ -7,9 +7,20 @@ using System.Threading.Tasks; namespace Serein.Library.Utils { + /// + /// 触发器结果类,用于存储触发器的类型和返回值。 + /// + /// public class TriggerResult { + /// + /// 触发类型 + /// public TriggerDescription Type { get; set; } + + /// + /// 触发结果值 + /// public TResult Value { get; set; } } } diff --git a/Library/Utils/JsonHelper.cs b/Library/Utils/JsonHelper.cs index 0b6d06d..bbcdd2f 100644 --- a/Library/Utils/JsonHelper.cs +++ b/Library/Utils/JsonHelper.cs @@ -23,28 +23,57 @@ namespace Serein.Library.Utils JsonHelper.provider = jsonPortal; } + /// + /// 反序列化Json文本为指定类型的对象 + /// + /// + /// + /// public static T Deserialize(string jsonText) { return provider.Deserialize(jsonText); } + /// + /// 反序列化Json文本为指定类型的对象 + /// + /// + /// + /// public static object Deserialize(string jsonText, Type type) { return provider.Deserialize(jsonText, type); } + /// + /// 解析Json文本为IJsonToken对象 + /// + /// + /// + public static IJsonToken Parse(string json) { return provider.Parse(json); } + /// + /// 将对象序列化为Json文本 + /// + /// + /// public static string Serialize(object obj) { return provider.Serialize(obj); } + + /// + /// 创建一个Json对象,使用字典初始化 + /// + /// + /// public static IJsonToken Object(Action> init) { var dict = new Dictionary(); @@ -52,11 +81,21 @@ namespace Serein.Library.Utils return provider.CreateObject(dict); } + /// + /// 创建一个Json对象,使用字典初始化 + /// + /// + /// public static IJsonToken Array(IEnumerable values) { return provider.CreateArray(values); } + /// + /// 将对象转换为JsonToken + /// + /// + /// public static IJsonToken FromObject(object obj) { if (obj is System.Collections.IEnumerable && !(obj is string)) diff --git a/Library/Utils/LinqHelper.cs b/Library/Utils/LinqHelper.cs index 3ac3de9..16192c8 100644 --- a/Library/Utils/LinqHelper.cs +++ b/Library/Utils/LinqHelper.cs @@ -32,6 +32,14 @@ namespace Serein.Library.Utils } } + /// + /// 异步选择器,返回一个新的集合,其中每个元素都是通过异步方法转换的结果。 + /// + /// + /// + /// + /// + /// public static async Task> SelectAsync(this IEnumerable source, Func> method) { @@ -39,6 +47,15 @@ namespace Serein.Library.Utils } + /// + /// 异步选择器,返回一个新的集合,其中每个元素都是通过异步方法转换的结果。 + /// + /// + /// + /// + /// + /// + /// public static async Task> SelectAsync(this IEnumerable source, Func> method, int concurrency = int.MaxValue) diff --git a/Library/Utils/ObjectPool.cs b/Library/Utils/ObjectPool.cs index 526f7e1..2e9ad40 100644 --- a/Library/Utils/ObjectPool.cs +++ b/Library/Utils/ObjectPool.cs @@ -28,23 +28,39 @@ namespace Serein.Library.Utils internal T Value; } - // 不使用System。Func{T},因为. net 2.0没有该类型。 + /// + /// 不使用System。Func{T},因为. net 2.0没有该类型。 + /// + /// public delegate T Factory(); - // 池对象的存储。第一个项存储在专用字段中,因为我们希望能够满足来自它的大多数请求。 + /// + /// 池对象的存储。第一个项存储在专用字段中,因为我们希望能够满足来自它的大多数请求。 + /// private T _firstItem; private readonly Element[] _items; - // 工厂在池的生命周期内被存储。只有当池需要扩展时,我们才调用它。 - // 与“new T()”相比,Func为实现者提供了更多的灵活性,并且比“new T()”更快。 + /// + /// 工厂在池的生命周期内被存储。只有当池需要扩展时,我们才调用它。 + /// 与“new T()”相比,Func为实现者提供了更多的灵活性,并且比“new T()”更快。 + /// private readonly Factory _factory; + /// + /// 创建一个新的对象池实例,使用指定的工厂函数和默认大小(处理器核心数的两倍)。 + /// + /// public ObjectPool(Factory factory) : this(factory, Environment.ProcessorCount * 2) { } + /// + /// 创建一个新的对象池实例,使用指定的工厂函数和指定的大小。 + /// + /// + /// public ObjectPool(Factory factory, int size) { Debug.Assert(size >= 1); @@ -52,6 +68,10 @@ namespace Serein.Library.Utils _items = new Element[size - 1]; } + /// + /// 创建一个新的实例。 + /// + /// private T CreateInstance() { T inst = _factory(); @@ -82,6 +102,10 @@ namespace Serein.Library.Utils return inst; } + /// + /// 慢速分配方法,当第一个元素不可用时调用。 + /// + /// private T AllocateSlow() { Element[] items = _items; diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index 1ed410c..dc06597 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -669,6 +669,12 @@ namespace Serein.Library.Utils #region 运行 + /// + /// 运行一个方法,方法的参数类型由IOC容器提供 + /// + /// + /// + /// public ISereinIOC Run(Action action) { var service = Get(); @@ -676,6 +682,13 @@ namespace Serein.Library.Utils return this; } + /// + /// 运行一个方法,方法的参数类型由IOC容器提供 + /// + /// + /// + /// + /// public ISereinIOC Run(Action action) { var service1 = Get(); @@ -685,6 +698,14 @@ namespace Serein.Library.Utils return this; } + /// + /// 运行一个方法,方法的参数类型由IOC容器提供 + /// + /// + /// + /// + /// + /// public ISereinIOC Run(Action action) { var service1 = Get(); @@ -694,6 +715,15 @@ namespace Serein.Library.Utils return this; } + /// + /// 运行一个方法,方法的参数类型由IOC容器提供 + /// + /// + /// + /// + /// + /// + /// public ISereinIOC Run(Action action) { var service1 = Get(); diff --git a/NodeFlow/Env/FlowControl.cs b/NodeFlow/Env/FlowControl.cs index 886c82d..95670d6 100644 --- a/NodeFlow/Env/FlowControl.cs +++ b/NodeFlow/Env/FlowControl.cs @@ -54,11 +54,12 @@ namespace Serein.NodeFlow.Env - private FlowWorkManagement flowWorkManagement; - private ISereinIOC externalIOC; - private Action setDefultMemberOnReset; + private FlowWorkManagement? flowWorkManagement; + private ISereinIOC? externalIOC; + private Action? setDefultMemberOnReset; private bool IsUseExternalIOC = false; - private object lockObj = new object(); + private readonly object lockObj = new object(); + /// /// 如果全局触发器还在运行,则为 Running 。 /// @@ -313,7 +314,7 @@ namespace Serein.NodeFlow.Env }*/ } /// - public void UseExternalIOC(ISereinIOC ioc, Action setDefultMemberOnReset = null) + public void UseExternalIOC(ISereinIOC ioc, Action? setDefultMemberOnReset = null) { IOC = ioc; // 设置IOC容器 this.setDefultMemberOnReset = setDefultMemberOnReset; diff --git a/NodeFlow/Env/FlowEdit.cs b/NodeFlow/Env/FlowEdit.cs index 45da21c..088e4be 100644 --- a/NodeFlow/Env/FlowEdit.cs +++ b/NodeFlow/Env/FlowEdit.cs @@ -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; /// @@ -74,17 +75,23 @@ namespace Serein.NodeFlow.Env /// /// 从Guid获取画布 /// - /// 节点Guid - /// 节点Model - /// 无法获取节点、Guid/节点为null时报错 - public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails) + /// 画布Guid + /// 画布model + /// 是否获取成功 + 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获取节点 /// /// 节点Guid - /// 节点Model - /// 无法获取节点、Guid/节点为null时报错 - public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel) + /// 节点Model + /// 是否获取成功 + public bool TryGetNodeModel(string nodeGuid, [NotNullWhen(true)]out IFlowNode? nodeModel) { if (string.IsNullOrEmpty(nodeGuid)) { @@ -176,7 +183,7 @@ namespace Serein.NodeFlow.Env /// /// 创建节点 /// - /// + /// private bool TryAddNode(IFlowNode nodeModel) { nodeModel.Guid ??= Guid.NewGuid().ToString(); @@ -218,7 +225,7 @@ namespace Serein.NodeFlow.Env } }; - flowOperationService.Execute(operation); + _ = flowOperationService.Execute(operation); } /// public void RemoveCanvas(string canvasGuid) @@ -227,7 +234,7 @@ namespace Serein.NodeFlow.Env { CanvasGuid = canvasGuid }; - flowOperationService.Execute(operation); + _ = flowOperationService.Execute(operation); } /// 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); } /// 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); } /// 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); } /// 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); } /// - 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); } /// 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); } /// 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); } /// 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); } /// 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; } /// @@ -376,7 +364,7 @@ namespace Serein.NodeFlow.Env ConnectionInvokeType = connectionType, ChangeType = NodeConnectChangeEventArgs.ConnectChangeType.Create }; - flowOperationService.Execute(operation); + _ = flowOperationService.Execute(operation); } /// 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); } /// diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 93918fa..62acad1 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -170,7 +170,6 @@ namespace Serein.NodeFlow.Env return (isConnect, remoteMsgUtil); }*/ - /// /* public async Task ExitFlowAsync() { return await currentFlowEnvironment.FlowControl.ExitFlowAsync(); @@ -190,9 +189,9 @@ namespace Serein.NodeFlow.Env }*/ /// - public async Task GetProjectInfoAsync() + public SereinProjectData GetProjectInfoAsync() { - return await currentFlowEnvironment.GetProjectInfoAsync(); + return currentFlowEnvironment.GetProjectInfoAsync(); } /// diff --git a/NodeFlow/Env/FlowEnvironmentEvent.cs b/NodeFlow/Env/FlowEnvironmentEvent.cs index 276150a..356fbee 100644 --- a/NodeFlow/Env/FlowEnvironmentEvent.cs +++ b/NodeFlow/Env/FlowEnvironmentEvent.cs @@ -3,112 +3,150 @@ using Serein.Library.Api; namespace Serein.NodeFlow.Env { + /// + /// 流程环境事件类 + /// 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; + /// + 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; + /// public void OnDllLoad(LoadDllEventArgs eventArgs) { DllLoad?.Invoke(eventArgs); } + /// public void OnProjectLoaded(ProjectLoadedEventArgs eventArgs) { ProjectLoaded?.Invoke(eventArgs); } + /// public void OnProjectSaving(ProjectSavingEventArgs eventArgs) { ProjectSaving?.Invoke(eventArgs); } + /// public void OnNodeConnectChanged(NodeConnectChangeEventArgs eventArgs) { NodeConnectChanged?.Invoke(eventArgs); } + /// public void OnCanvasCreated(CanvasCreateEventArgs eventArgs) { CanvasCreated?.Invoke(eventArgs); } + /// public void OnCanvasRemoved(CanvasRemoveEventArgs eventArgs) { CanvasRemoved?.Invoke(eventArgs); } + /// public void OnNodeCreated(NodeCreateEventArgs eventArgs) { NodeCreated?.Invoke(eventArgs); } + /// public void OnNodeRemoved(NodeRemoveEventArgs eventArgs) { NodeRemoved?.Invoke(eventArgs); } - + /// public void OnNodePlace(NodePlaceEventArgs eventArgs) { NodePlace?.Invoke(eventArgs); } + /// public void OnNodeTakeOut(NodeTakeOutEventArgs eventArgs) { NodeTakeOut?.Invoke(eventArgs); } + /// public void OnStartNodeChanged(StartNodeChangeEventArgs eventArgs) { StartNodeChanged?.Invoke(eventArgs); } + /// public void OnFlowRunComplete(FlowEventArgs eventArgs) { FlowRunComplete?.Invoke(eventArgs); } + /// public void OnMonitorObjectChanged(MonitorObjectEventArgs eventArgs) { MonitorObjectChanged?.Invoke(eventArgs); } + /// public void OnNodeInterruptStateChanged(NodeInterruptStateChangeEventArgs eventArgs) { NodeInterruptStateChanged?.Invoke(eventArgs); } + /// public void OnInterruptTriggered(InterruptTriggerEventArgs eventArgs) { InterruptTriggered?.Invoke(eventArgs); } + /// public void OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs) { IOCMembersChanged?.Invoke(eventArgs); } + /// public void OnNodeLocated(NodeLocatedEventArgs eventArgs) { NodeLocated?.Invoke(eventArgs); } + /// public void OnEnvOutput(InfoType type, string value) { EnvOutput?.Invoke(type, value); diff --git a/NodeFlow/Env/LocalFlowEnvironment.cs b/NodeFlow/Env/LocalFlowEnvironment.cs index 42e96b6..91701f5 100644 --- a/NodeFlow/Env/LocalFlowEnvironment.cs +++ b/NodeFlow/Env/LocalFlowEnvironment.cs @@ -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 /// 打开远程管理 /// /// - 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 /// /// UI线程操作类 /// - public UIContextOperation UIContextOperation { get; private set; } + public UIContextOperation? UIContextOperation { get; private set; } /// /// 节点MVVM管理服务 @@ -173,15 +174,10 @@ namespace Serein.NodeFlow.Env #region 私有变量 - /// - /// 装饰器运行环境类 - /// - private readonly IFlowEnvironment mainFlowEnvironment; - /// /// 流程运行时的IOC容器 /// - private ISereinIOC flowRunIOC; + private ISereinIOC? flowRunIOC; /// /// local环境的IOC容器,主要用于注册本地环境的服务 @@ -203,28 +199,7 @@ namespace Serein.NodeFlow.Env /// private readonly FlowModelService _flowModelService; - /* /// - /// 环境加载的节点集合 - /// Node Guid - Node Model - /// - private Dictionary NodeModels { get; } = []; - /// - /// 运行环境加载的画布集合 - /// - private Dictionary FlowCanvass { get; } = []; - - /// - /// 存放触发器节点(运行时全部调用) - /// - private List FlipflopNodes { get; } = []; - */ - - - /// - /// 流程任务管理 - /// - private FlowWorkManagement? flowTaskManagement; #endregion @@ -247,34 +222,15 @@ namespace Serein.NodeFlow.Env } - /// - /// 获取当前环境信息(远程连接) - /// - /// - public async Task GetEnvInfoAsync() - { - // 获取所有的程序集对应的方法信息(程序集相关的数据) - var libraryMdss = this._flowLibraryService.GetAllLibraryMds().ToArray(); - // 获取当前项目的信息(节点相关的数据) - var project = await GetProjectInfoAsync(); // 远程连接获取远程环境项目信息 - SereinEnv.WriteLine(InfoType.INFO, "已将当前环境信息发送到远程客户端"); - return new FlowEnvInfo - { - Project = project, // 项目信息 - LibraryMds = libraryMdss, // 环境方法 - }; - } - /// /// 保存项目 /// 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()); } - /// - /// 加载远程环境 - /// - /// 远程环境地址 - /// 远程环境端口 - /// 密码 - /*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);*//* - }*/ - - /// - /// 退出远程环境 - /// - public void ExitRemoteEnv() - { - IsControlRemoteEnv = false; - } - /// /// 序列化当前项目的依赖信息、节点信息 /// /// - public async Task GetProjectInfoAsync() + public SereinProjectData GetProjectInfoAsync() { var projectData = new SereinProjectData() { @@ -466,28 +369,6 @@ namespace Serein.NodeFlow.Env } } - /* /// - /// 加载本地程序集 - /// - /// - 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}"); - } - - }*/ - - /// /// 移除DLL /// @@ -572,9 +453,6 @@ namespace Serein.NodeFlow.Env //} } - private int _addCanvasCount = 0; - - private async Task LoadCanvasAsync(FlowCanvasDetailsInfo info) { var model = new FlowCanvasDetails(this); @@ -623,6 +501,7 @@ namespace Serein.NodeFlow.Env /// 异步方法:Func<object,object[],Task> /// 异步有返回值方法:Func<object,object[],Task<object>> /// + /// /// /// /// @@ -711,10 +590,11 @@ namespace Serein.NodeFlow.Env /// /// 从Guid获取画布 /// - /// 节点Guid - /// 节点Model + /// 画布Guid + /// 画布实体 + /// 是否获取成功 /// 无法获取节点、Guid/节点为null时报错 - 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获取节点 /// /// 节点Guid + /// 节点Guid /// 节点Model /// 无法获取节点、Guid/节点为null时报错 - public bool TryGetNodeModel(string nodeGuid, out IFlowNode nodeModel) + public bool TryGetNodeModel(string nodeGuid, [NotNullWhen(true)] out IFlowNode? nodeModel) { if (string.IsNullOrEmpty(nodeGuid)) { diff --git a/NodeFlow/FlowNodeExtension.cs b/NodeFlow/FlowNodeExtension.cs index 1f1cc3f..f880a2d 100644 --- a/NodeFlow/FlowNodeExtension.cs +++ b/NodeFlow/FlowNodeExtension.cs @@ -158,10 +158,11 @@ namespace Serein.NodeFlow /// 字符串构建器 /// 缩进次数(4个空格) /// 要添加的代码 + /// 是否换行 /// 字符串构建器本身 public static StringBuilder AppendCode(this StringBuilder sb, int retractCount = 0, - string code = null, + string? code = null, bool isWrapping = true) { if (!string.IsNullOrWhiteSpace(code)) diff --git a/NodeFlow/FlowWorkOptions.cs b/NodeFlow/FlowWorkOptions.cs index 171c7af..5a5a4bb 100644 --- a/NodeFlow/FlowWorkOptions.cs +++ b/NodeFlow/FlowWorkOptions.cs @@ -4,22 +4,25 @@ using Serein.Library.Api; namespace Serein.NodeFlow { + /// + /// 流程任务类,包含流程的起始节点和所有节点信息。 + /// public class FlowTask { /// /// 是否异步启动流程 /// - public bool IsTaskAsync { get; set; } + public bool IsTaskAsync { get; set; } /// /// 流程起始节点 /// - public Func GetStartNode { get; set; } + public Func? GetStartNode { get; set; } /// /// 获取当前画布流程的所有节点 /// - public Func> GetNodes { get; set; } + public Func>? GetNodes { get; set; } } /// @@ -30,11 +33,11 @@ namespace Serein.NodeFlow /// /// 流程IOC容器 /// - public ISereinIOC FlowIOC { get; set; } + public required ISereinIOC FlowIOC { get; set; } /// /// 流程运行环境 /// - public IFlowEnvironment Environment { get; set; }// = environment; + public required IFlowEnvironment Environment { get; set; }// = environment; /// /// 表示运行环境状态 @@ -44,35 +47,30 @@ namespace Serein.NodeFlow /// /// 上下文线程池 /// - public Serein.Library.Utils.ObjectPool FlowContextPool { get; set; } + public required Serein.Library.Utils.ObjectPool FlowContextPool { get; set; } /// /// 每个画布需要启用的节点 /// - public Dictionary Flows { get; set; } - - /// - /// 当前任务加载的所有节点 - /// - //public List Nodes { get; set; }// = nodes; + public Dictionary Flows { get; set; } = []; /// /// 需要注册的类型 /// - public Dictionary> AutoRegisterTypes { get; set; } //= autoRegisterTypes; + public Dictionary> AutoRegisterTypes { get; set; } = []; /// /// 初始化时需要的方法 /// - public List InitMds { get; set; }// = initMds; + public List InitMds { get; set; } = []; /// /// 加载时需要的方法 /// - public List LoadMds { get; set; }// = loadMds; + public List LoadMds { get; set; } = []; /// /// 退出时需要调用的方法 /// - public List ExitMds { get; set; } //= exitMds; + public List ExitMds { get; set; } = []; } } diff --git a/NodeFlow/Model/Librarys/FlowLibraryCache.cs b/NodeFlow/Model/Librarys/FlowLibraryCache.cs index 58fb0f9..3d7da56 100644 --- a/NodeFlow/Model/Librarys/FlowLibraryCache.cs +++ b/NodeFlow/Model/Librarys/FlowLibraryCache.cs @@ -143,7 +143,7 @@ namespace Serein.NodeFlow.Model.Library // 从 scanTypes.Type 创建的方法信息 // Md : 方法描述 // Dd :方法对应的Emit委托 - List detailss = new List(); + List detailss = new List(); // 遍历扫描的类型 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)); } } diff --git a/NodeFlow/Model/Librarys/LibraryMdDd.cs b/NodeFlow/Model/Librarys/LibraryMdDd.cs deleted file mode 100644 index 79cda80..0000000 --- a/NodeFlow/Model/Librarys/LibraryMdDd.cs +++ /dev/null @@ -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; - } - } - -} diff --git a/NodeFlow/Model/Librarys/LibraryMthodInfo.cs b/NodeFlow/Model/Librarys/LibraryMthodInfo.cs new file mode 100644 index 0000000..7e48680 --- /dev/null +++ b/NodeFlow/Model/Librarys/LibraryMthodInfo.cs @@ -0,0 +1,39 @@ +using Serein.Library; +using System.Reflection; + +namespace Serein.NodeFlow.Model.Library +{ + /// + /// 库方法信息类,包含方法的详细信息、反射信息和委托信息。 + /// + public class LibraryMthodInfo + { + /// + /// 方法的详细信息。 + /// + public MethodDetails MethodDetails { get; } + + /// + /// 方法的反射信息,包含方法名称、参数类型等。 + /// + public MethodInfo MethodInfo { get; } + /// + /// Emit构造委托 + /// + public DelegateDetails DelegateDetails { get; } + + /// + /// 构造一个新的库方法信息实例。 + /// + /// + /// + /// + public LibraryMthodInfo(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails) + { + MethodDetails = methodDetails; + MethodInfo = methodInfo; + DelegateDetails = delegateDetails; + } + } + +} diff --git a/NodeFlow/Model/Node/FlowModelExtension.cs b/NodeFlow/Model/Nodes/FlowModelExtension.cs similarity index 77% rename from NodeFlow/Model/Node/FlowModelExtension.cs rename to NodeFlow/Model/Nodes/FlowModelExtension.cs index 0b545a1..d42e35a 100644 --- a/NodeFlow/Model/Node/FlowModelExtension.cs +++ b/NodeFlow/Model/Nodes/FlowModelExtension.cs @@ -140,7 +140,6 @@ namespace Serein.NodeFlow.Model.Nodes /// 从节点信息加载节点 /// /// - /// /// /// public static void LoadInfo(this IFlowNode nodeModel, NodeInfo nodeInfo) @@ -243,97 +242,6 @@ namespace Serein.NodeFlow.Model.Nodes } } - /// - /// 检查监视表达式是否生效 - /// - /// 节点Moel - /// 上下文 - /// 新的数据 - /// - /*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; // 替换数据 - } - }*/ - - /// - /// 监视对象表达式中断 - /// - /// - /// - /// - /// - /// - /*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; - // } - // } - // } - - //} - }*/ - - - /// - /// 不再中断 - /// - public static void CancelInterrupt(IFlowNode nodeModel) - { - nodeModel.DebugSetting.IsInterrupt = false; - nodeModel.DebugSetting.CancelInterrupt?.Invoke(); - } - #if DEBUG /// /// 程序集更新,更新节点方法描述、以及所有入参描述的类型 diff --git a/NodeFlow/Model/Node/NodeModelBaseData.cs b/NodeFlow/Model/Nodes/NodeModelBaseData.cs similarity index 90% rename from NodeFlow/Model/Node/NodeModelBaseData.cs rename to NodeFlow/Model/Nodes/NodeModelBaseData.cs index 00e3a8f..08e6a12 100644 --- a/NodeFlow/Model/Node/NodeModelBaseData.cs +++ b/NodeFlow/Model/Nodes/NodeModelBaseData.cs @@ -13,49 +13,49 @@ namespace Serein.NodeFlow.Model.Nodes /// /// 节点基类(数据) /// - [NodeProperty(ValuePath = NodeValuePath.Node)] + [FlowDataProperty(ValuePath = NodeValuePath.Node)] public abstract partial class NodeModelBase : IFlowNode { /// /// 节点运行环境 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private IFlowEnvironment _env; /// /// 标识节点对象全局唯一 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private string _guid; /// /// 描述节点对应的控件类型 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private NodeControlType _controlType; /// /// 所属画布 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private FlowCanvasDetails _canvasDetails ; /// /// 在画布中的位置 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private PositionOfUI _position ; /// /// 显示名称 /// - [PropertyInfo] + [DataInfo] private string _displayName; /// /// 是否公开 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private bool _isPublic; /* /// @@ -67,13 +67,13 @@ namespace Serein.NodeFlow.Model.Nodes /// /// 附加的调试功能 /// - [PropertyInfo(IsProtection = true)] + [DataInfo(IsProtection = true)] private NodeDebugSetting _debugSetting ; /// /// 方法描述。包含参数信息。不包含Method与委托,如若需要调用对应的方法,需要通过MethodName从环境中获取委托进行调用。 /// - [PropertyInfo] + [DataInfo] private MethodDetails _methodDetails ; } @@ -90,6 +90,10 @@ namespace Serein.NodeFlow.Model.Nodes /// public virtual int MaxChildrenCount { get; } = 0; + /// + /// 创建一个节点模型的基类实例 + /// + /// public NodeModelBase(IFlowEnvironment environment) { PreviousNodes = new Dictionary>(); diff --git a/NodeFlow/Model/Node/NodeModelBaseFunc.cs b/NodeFlow/Model/Nodes/NodeModelBaseFunc.cs similarity index 99% rename from NodeFlow/Model/Node/NodeModelBaseFunc.cs rename to NodeFlow/Model/Nodes/NodeModelBaseFunc.cs index 17c252e..57d6d1b 100644 --- a/NodeFlow/Model/Node/NodeModelBaseFunc.cs +++ b/NodeFlow/Model/Nodes/NodeModelBaseFunc.cs @@ -47,7 +47,6 @@ namespace Serein.NodeFlow.Model.Nodes /// /// 流程上下文 /// - /// 自定义参数 /// 节点传回数据对象 public virtual async Task ExecutingAsync(IFlowContext context, CancellationToken token) { diff --git a/NodeFlow/Model/Node/SingleActionNode.cs b/NodeFlow/Model/Nodes/SingleActionNode.cs similarity index 72% rename from NodeFlow/Model/Node/SingleActionNode.cs rename to NodeFlow/Model/Nodes/SingleActionNode.cs index 1720229..1b1084e 100644 --- a/NodeFlow/Model/Node/SingleActionNode.cs +++ b/NodeFlow/Model/Nodes/SingleActionNode.cs @@ -9,6 +9,10 @@ namespace Serein.NodeFlow.Model.Nodes /// public class SingleActionNode : NodeModelBase { + /// + /// 构造一个新的单动作节点实例。 + /// + /// public SingleActionNode(IFlowEnvironment environment):base(environment) { diff --git a/NodeFlow/Model/Node/SingleConditionNode.cs b/NodeFlow/Model/Nodes/SingleConditionNode.cs similarity index 95% rename from NodeFlow/Model/Node/SingleConditionNode.cs rename to NodeFlow/Model/Nodes/SingleConditionNode.cs index bebe12c..38a9f10 100644 --- a/NodeFlow/Model/Node/SingleConditionNode.cs +++ b/NodeFlow/Model/Nodes/SingleConditionNode.cs @@ -9,25 +9,25 @@ namespace Serein.NodeFlow.Model.Nodes /// /// 条件节点(用于条件控件) /// - [NodeProperty(ValuePath = NodeValuePath.Node)] + [FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)] public partial class SingleConditionNode : NodeModelBase { /// /// 是否为自定义参数 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private bool _isExplicitData; /// /// 自定义参数值 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string? _explicitData; /// /// 条件表达式 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string _expression; } @@ -44,7 +44,10 @@ namespace Serein.NodeFlow.Model.Nodes /// private const int INDEX_EXPRESSION = 0; - + /// + /// 条件节点构造函数 + /// + /// public SingleConditionNode(IFlowEnvironment environment):base(environment) { this.IsExplicitData = false; @@ -52,6 +55,9 @@ namespace Serein.NodeFlow.Model.Nodes this.Expression = "PASS"; } + /// + /// 创建节点时调用的方法 + /// public override void OnCreating() { // 这里的这个参数是为了方便使用入参控制点,参数无意义 @@ -106,6 +112,7 @@ namespace Serein.NodeFlow.Model.Nodes /// 重写节点的方法执行 /// /// + /// /// public override async Task ExecutingAsync(IFlowContext context, CancellationToken token) { diff --git a/NodeFlow/Model/Node/SingleExpOpNode.cs b/NodeFlow/Model/Nodes/SingleExpOpNode.cs similarity index 92% rename from NodeFlow/Model/Node/SingleExpOpNode.cs rename to NodeFlow/Model/Nodes/SingleExpOpNode.cs index e513450..de1cd92 100644 --- a/NodeFlow/Model/Node/SingleExpOpNode.cs +++ b/NodeFlow/Model/Nodes/SingleExpOpNode.cs @@ -8,19 +8,21 @@ namespace Serein.NodeFlow.Model.Nodes /// /// Expression Operation - 表达式操作 /// - [NodeProperty(ValuePath = NodeValuePath.Node)] + [FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)] public partial class SingleExpOpNode : NodeModelBase { /// /// 表达式 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string _expression; } - + /// + /// 表达式节点模型基类 + /// public partial class SingleExpOpNode : NodeModelBase { /// @@ -33,6 +35,10 @@ namespace Serein.NodeFlow.Model.Nodes /// private const int INDEX_EXPRESSION = 0; + /// + /// 表达式节点构造函数 + /// + /// public SingleExpOpNode(IFlowEnvironment environment) : base(environment) { @@ -87,7 +93,12 @@ namespace Serein.NodeFlow.Model.Nodes this.Expression = nodeInfo.CustomData?.Expression ?? ""; } - + /// + /// 执行节点操作 + /// + /// + /// + /// public override async Task ExecutingAsync(IFlowContext context, CancellationToken token) { if(token.IsCancellationRequested) return FlowResult.Fail(this.Guid, context, "流程已通过token取消"); diff --git a/NodeFlow/Model/Node/SingleFlipflopNode.cs b/NodeFlow/Model/Nodes/SingleFlipflopNode.cs similarity index 93% rename from NodeFlow/Model/Node/SingleFlipflopNode.cs rename to NodeFlow/Model/Nodes/SingleFlipflopNode.cs index 242878c..6283a8b 100644 --- a/NodeFlow/Model/Node/SingleFlipflopNode.cs +++ b/NodeFlow/Model/Nodes/SingleFlipflopNode.cs @@ -10,7 +10,10 @@ namespace Serein.NodeFlow.Model.Nodes /// public class SingleFlipflopNode : NodeModelBase { - + /// + /// 构造一个新的单触发器节点实例。 + /// + /// public SingleFlipflopNode(IFlowEnvironment environment) : base(environment) { @@ -21,6 +24,7 @@ namespace Serein.NodeFlow.Model.Nodes /// 执行触发器进行等待触发 /// /// + /// /// /// public override async Task ExecutingAsync(IFlowContext context, CancellationToken token) diff --git a/NodeFlow/Model/Node/SingleFlowCallNode.cs b/NodeFlow/Model/Nodes/SingleFlowCallNode.cs similarity index 94% rename from NodeFlow/Model/Node/SingleFlowCallNode.cs rename to NodeFlow/Model/Nodes/SingleFlowCallNode.cs index e7dc7e8..06d9a2e 100644 --- a/NodeFlow/Model/Node/SingleFlowCallNode.cs +++ b/NodeFlow/Model/Nodes/SingleFlowCallNode.cs @@ -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 { /// /// 目标公开节点 /// - [PropertyInfo(IsNotification = true)] - private string targetNodeGuid; + [DataInfo(IsNotification = true)] + private string targetNodeGuid = string.Empty; /// /// 使用目标节点的参数(如果为true,则使用目标节点的入参,如果为false,则使用节点自定义入参) /// - [PropertyInfo(IsNotification = true)] - private bool _isShareParam ; + [DataInfo(IsNotification = true)] + private bool _isShareParam ; /// /// 接口全局名称 /// - [PropertyInfo(IsNotification = true)] - private string _apiGlobalName; + [DataInfo(IsNotification = true)] + private string _apiGlobalName = string.Empty; } @@ -45,6 +45,10 @@ namespace Serein.NodeFlow.Model.Nodes /// public MethodDetails CacheMethodDetails { get; private set; } + /// + /// 流程接口节点 + /// + /// public SingleFlowCallNode(IFlowEnvironment environment) : base(environment) { @@ -207,7 +211,7 @@ namespace Serein.NodeFlow.Model.Nodes } private static Dictionary ApiInvokeNameCache = new Dictionary(); - 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}"; } } + + /// + /// 获取流程接口节点的名称 + /// + /// + /// public static string GetApiInvokeName(SingleFlowCallNode node) { if(node.TargetNode is null) diff --git a/NodeFlow/Model/Node/SingleGlobalDataNode.cs b/NodeFlow/Model/Nodes/SingleGlobalDataNode.cs similarity index 89% rename from NodeFlow/Model/Node/SingleGlobalDataNode.cs rename to NodeFlow/Model/Nodes/SingleGlobalDataNode.cs index 4e7074d..8584346 100644 --- a/NodeFlow/Model/Node/SingleGlobalDataNode.cs +++ b/NodeFlow/Model/Nodes/SingleGlobalDataNode.cs @@ -9,13 +9,13 @@ namespace Serein.NodeFlow.Model.Nodes /// /// Expression Operation - 表达式操作 /// - [NodeProperty(ValuePath = NodeValuePath.Node)] + [FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)] public partial class SingleGlobalDataNode : NodeModelBase { /// /// 全局数据的Key名称 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string _keyName; } @@ -36,6 +36,10 @@ namespace Serein.NodeFlow.Model.Nodes /// public override int MaxChildrenCount => 1; + /// + /// 全局数据节点,允许放置一个数据源节点,通常是[Action]或[Script]节点,用于获取全局数据。 + /// + /// public SingleGlobalDataNode(IFlowEnvironment environment) : base(environment) { } @@ -85,7 +89,11 @@ namespace Serein.NodeFlow.Model.Nodes } - + /// + /// 从容器中取出节点 + /// + /// + /// public bool TakeOutNode(IFlowNode nodeModel) { if (ChildrenNode.Contains(nodeModel)) @@ -102,7 +110,10 @@ namespace Serein.NodeFlow.Model.Nodes } - public async void TakeOutAll() + /// + /// 从容器中取出所有节点 + /// + public void TakeOutAll() { foreach (var nodeModel in ChildrenNode) { @@ -111,12 +122,13 @@ namespace Serein.NodeFlow.Model.Nodes DataNode = null; } - + /// /// 设置全局数据 /// /// + /// /// public override async Task ExecutingAsync(IFlowContext context, CancellationToken token) { diff --git a/NodeFlow/Model/Node/SingleNetScriptNode.cs b/NodeFlow/Model/Nodes/SingleNetScriptNode.cs similarity index 57% rename from NodeFlow/Model/Node/SingleNetScriptNode.cs rename to NodeFlow/Model/Nodes/SingleNetScriptNode.cs index 011cb17..756cae3 100644 --- a/NodeFlow/Model/Node/SingleNetScriptNode.cs +++ b/NodeFlow/Model/Nodes/SingleNetScriptNode.cs @@ -10,27 +10,30 @@ using System.Threading.Tasks; namespace Serein.NodeFlow.Model.Nodes { + /// + /// 单脚本节点(用于脚本控件) + /// - [NodeProperty(ValuePath = NodeValuePath.Node)] + [FlowDataProperty(ValuePath = NodeValuePath.Node, IsNodeImp = true)] public partial class SingleNetScriptNode : NodeModelBase { /// /// 脚本代码 /// - [PropertyInfo(IsNotification = true)] - private string _script; + [DataInfo(IsNotification = true)] + private string _script = string.Empty; /// /// 功能提示 /// - [PropertyInfo(IsNotification = true)] + [DataInfo(IsNotification = true)] private string _tips = "写一下提示吧"; /// /// 依赖路径 /// - [PropertyInfo(IsNotification = true)] - private List _libraryFilePaths; + [DataInfo(IsNotification = true)] + private List _libraryFilePaths = []; } @@ -41,46 +44,16 @@ namespace Serein.NodeFlow.Model.Nodes /// public override bool IsBase => true; + /// + /// 脚本代码 + /// + /// 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 = "脚本节点入参" - - //}; - - } - /// /// 导出脚本代码 /// diff --git a/NodeFlow/Model/Node/SingleScriptNode.cs b/NodeFlow/Model/Nodes/SingleScriptNode.cs similarity index 98% rename from NodeFlow/Model/Node/SingleScriptNode.cs rename to NodeFlow/Model/Nodes/SingleScriptNode.cs index f627226..e7700ca 100644 --- a/NodeFlow/Model/Node/SingleScriptNode.cs +++ b/NodeFlow/Model/Nodes/SingleScriptNode.cs @@ -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; } /// @@ -293,6 +293,7 @@ namespace Serein.NodeFlow.Model.Nodes /// 执行脚本 /// /// + /// /// public override async Task ExecutingAsync(IFlowContext context, CancellationToken token) { diff --git a/NodeFlow/Model/Node/SingleUINode.cs b/NodeFlow/Model/Nodes/SingleUINode.cs similarity index 66% rename from NodeFlow/Model/Node/SingleUINode.cs rename to NodeFlow/Model/Nodes/SingleUINode.cs index 49f0c7a..171aa51 100644 --- a/NodeFlow/Model/Node/SingleUINode.cs +++ b/NodeFlow/Model/Nodes/SingleUINode.cs @@ -8,13 +8,30 @@ using System.Threading.Tasks; namespace Serein.NodeFlow.Model.Nodes { + /// + /// 单个UI节点,适用于需要在流程中嵌入用户自定义控件的场景。 + /// public class SingleUINode : NodeModelBase { - public IEmbeddedContent Adapter { get; private set; } + /// + /// 适配的UI控件,必须实现IEmbeddedContent接口。 + /// + public IEmbeddedContent? Adapter { get; private set; } + + /// + /// 单个UI节点构造函数,初始化流程环境。 + /// + /// public SingleUINode(IFlowEnvironment environment) : base(environment) { } + /// + /// 执行节点逻辑,适用于嵌入式UI控件的流程节点。 + /// + /// + /// + /// public override async Task ExecutingAsync(IFlowContext context, CancellationToken token) { if (token.IsCancellationRequested) return FlowResult.Fail(this.Guid, context, "流程已通过token取消"); diff --git a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs b/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs similarity index 96% rename from NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs rename to NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs index e6a26c1..31e4f55 100644 --- a/NodeFlow/Model/Operation/ChangeNodeConnectionOperation.cs +++ b/NodeFlow/Model/Operations/ChangeNodeConnectionOperation.cs @@ -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 { /// /// 节点连接状态发生改变 @@ -45,6 +45,7 @@ namespace Serein.NodeFlow.Model.Operation /// public JunctionType ToNodeJunctionType { get; set; } + /// /// 连接类型 /// 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 /// private async Task 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 /// private async Task 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 /// /// private async Task 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 /// /// 移除参数连接关系 /// - /// - /// - /// private async Task 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); diff --git a/NodeFlow/Model/Operation/ChangeParameterOperation.cs b/NodeFlow/Model/Operations/ChangeParameterOperation.cs similarity index 98% rename from NodeFlow/Model/Operation/ChangeParameterOperation.cs rename to NodeFlow/Model/Operations/ChangeParameterOperation.cs index 5cd2d93..8ef1961 100644 --- a/NodeFlow/Model/Operation/ChangeParameterOperation.cs +++ b/NodeFlow/Model/Operations/ChangeParameterOperation.cs @@ -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 { diff --git a/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs b/NodeFlow/Model/Operations/ContainerPlaceNodeOperation.cs similarity index 98% rename from NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs rename to NodeFlow/Model/Operations/ContainerPlaceNodeOperation.cs index be4c65e..015006d 100644 --- a/NodeFlow/Model/Operation/ContainerPlaceNodeOperation.cs +++ b/NodeFlow/Model/Operations/ContainerPlaceNodeOperation.cs @@ -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 { /// /// 放置节点操作 diff --git a/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs b/NodeFlow/Model/Operations/ContainerTakeOutNodeOperation.cs similarity index 98% rename from NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs rename to NodeFlow/Model/Operations/ContainerTakeOutNodeOperation.cs index 2b206f4..81624f3 100644 --- a/NodeFlow/Model/Operation/ContainerTakeOutNodeOperation.cs +++ b/NodeFlow/Model/Operations/ContainerTakeOutNodeOperation.cs @@ -5,7 +5,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Serein.NodeFlow.Model.Operation +namespace Serein.NodeFlow.Model.Operations { /// diff --git a/NodeFlow/Model/Operation/CreateCanvasOperation.cs b/NodeFlow/Model/Operations/CreateCanvasOperation.cs similarity index 97% rename from NodeFlow/Model/Operation/CreateCanvasOperation.cs rename to NodeFlow/Model/Operations/CreateCanvasOperation.cs index 54bdc4c..5f51293 100644 --- a/NodeFlow/Model/Operation/CreateCanvasOperation.cs +++ b/NodeFlow/Model/Operations/CreateCanvasOperation.cs @@ -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 { diff --git a/NodeFlow/Model/Operation/CreateNodeOperation.cs b/NodeFlow/Model/Operations/CreateNodeOperation.cs similarity index 99% rename from NodeFlow/Model/Operation/CreateNodeOperation.cs rename to NodeFlow/Model/Operations/CreateNodeOperation.cs index e90cd78..f85d97c 100644 --- a/NodeFlow/Model/Operation/CreateNodeOperation.cs +++ b/NodeFlow/Model/Operations/CreateNodeOperation.cs @@ -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 { diff --git a/NodeFlow/Model/Operation/OperationBase.cs b/NodeFlow/Model/Operations/OperationBase.cs similarity index 98% rename from NodeFlow/Model/Operation/OperationBase.cs rename to NodeFlow/Model/Operations/OperationBase.cs index 1cc5e09..bffb958 100644 --- a/NodeFlow/Model/Operation/OperationBase.cs +++ b/NodeFlow/Model/Operations/OperationBase.cs @@ -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;} /// diff --git a/NodeFlow/Model/Operation/RemoveCanvasOperation.cs b/NodeFlow/Model/Operations/RemoveCanvasOperation.cs similarity index 97% rename from NodeFlow/Model/Operation/RemoveCanvasOperation.cs rename to NodeFlow/Model/Operations/RemoveCanvasOperation.cs index 2b21ce6..81aca5f 100644 --- a/NodeFlow/Model/Operation/RemoveCanvasOperation.cs +++ b/NodeFlow/Model/Operations/RemoveCanvasOperation.cs @@ -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 { diff --git a/NodeFlow/Model/Operation/RemoveNodeOperation.cs b/NodeFlow/Model/Operations/RemoveNodeOperation.cs similarity index 96% rename from NodeFlow/Model/Operation/RemoveNodeOperation.cs rename to NodeFlow/Model/Operations/RemoveNodeOperation.cs index e812d87..aedf894 100644 --- a/NodeFlow/Model/Operation/RemoveNodeOperation.cs +++ b/NodeFlow/Model/Operations/RemoveNodeOperation.cs @@ -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 /// 节点所在画布 /// private FlowCanvasDetails flowCanvasDetails; + /// /// 被删除的节点 /// @@ -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 移除方法调用关系 // 检查该节点的前继节点,然后从这些前继节点中移除与该节点的连接关系 diff --git a/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs b/NodeFlow/Model/Operations/SetConnectPriorityInvokeOperation.cs similarity index 98% rename from NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs rename to NodeFlow/Model/Operations/SetConnectPriorityInvokeOperation.cs index a8a6772..add154c 100644 --- a/NodeFlow/Model/Operation/SetConnectPriorityInvokeOperation.cs +++ b/NodeFlow/Model/Operations/SetConnectPriorityInvokeOperation.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Serein.NodeFlow.Model.Operation +namespace Serein.NodeFlow.Model.Operations { /// diff --git a/NodeFlow/Model/Operation/SetStartNodeOperation.cs b/NodeFlow/Model/Operations/SetStartNodeOperation.cs similarity index 98% rename from NodeFlow/Model/Operation/SetStartNodeOperation.cs rename to NodeFlow/Model/Operations/SetStartNodeOperation.cs index 5dfb7a7..9584400 100644 --- a/NodeFlow/Model/Operation/SetStartNodeOperation.cs +++ b/NodeFlow/Model/Operations/SetStartNodeOperation.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Serein.NodeFlow.Model.Operation +namespace Serein.NodeFlow.Model.Operations { /// /// 设置起始节点 diff --git a/NodeFlow/ScriptFlowApi.cs b/NodeFlow/ScriptFlowApi.cs index 97150af..8705278 100644 --- a/NodeFlow/ScriptFlowApi.cs +++ b/NodeFlow/ScriptFlowApi.cs @@ -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); - //} - + /// + /// 获取全局数据 + /// + /// + /// public object? GetGlobalData(string keyName) { return SereinEnv.GetFlowGlobalData(keyName); diff --git a/NodeFlow/Serein.NodeFlow.csproj b/NodeFlow/Serein.NodeFlow.csproj index 95807ca..2be26fe 100644 --- a/NodeFlow/Serein.NodeFlow.csproj +++ b/NodeFlow/Serein.NodeFlow.csproj @@ -1,7 +1,7 @@  - 1.2.2 + 1.2.3 net8.0 enable enable @@ -54,6 +54,7 @@ + diff --git a/NodeFlow/Services/CoreGenerateExtension.cs b/NodeFlow/Services/CoreGenerateExtension.cs index 0190a21..5f46656 100644 --- a/NodeFlow/Services/CoreGenerateExtension.cs +++ b/NodeFlow/Services/CoreGenerateExtension.cs @@ -77,6 +77,7 @@ namespace Serein.NodeFlow.Services /// 生成完全的xml注释 /// /// + /// /// public static string ToXmlComments(this string context, int retractCount = 0) { diff --git a/NodeFlow/Services/FlowApiService.cs b/NodeFlow/Services/FlowApiService.cs index cee0599..e8c9137 100644 --- a/NodeFlow/Services/FlowApiService.cs +++ b/NodeFlow/Services/FlowApiService.cs @@ -11,7 +11,7 @@ namespace Serein.NodeFlow.Services /// /// 流程API服务,用于外部调用流程接口 /// - public class FlowApiService + internal class FlowApiService { private readonly IFlowEnvironment flowEnvironment; private readonly FlowModelService flowModelService; diff --git a/NodeFlow/Services/FlowCoreGenerateService.cs b/NodeFlow/Services/FlowCoreGenerateService.cs index 7c91ea5..156a9b0 100644 --- a/NodeFlow/Services/FlowCoreGenerateService.cs +++ b/NodeFlow/Services/FlowCoreGenerateService.cs @@ -20,6 +20,11 @@ namespace Serein.NodeFlow.Services private readonly FlowModelService flowModelService; private readonly FlowLibraryService flowLibraryService; + /// + /// 流程代码生成服务 + /// + /// + /// 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 { /// /// 全局数据节点 /// - public SingleGlobalDataNode Node { get; set; } + public required SingleGlobalDataNode Node { get; set; } /// /// 全局数据的来源节点 /// - public IFlowNode DataSourceNode { get; set; } + public required IFlowNode DataSourceNode { get; set; } /// /// 全局数据的键名 diff --git a/NodeFlow/Services/FlowLibraryService.cs b/NodeFlow/Services/FlowLibraryService.cs index 2dbb313..499ca4d 100644 --- a/NodeFlow/Services/FlowLibraryService.cs +++ b/NodeFlow/Services/FlowLibraryService.cs @@ -14,6 +14,10 @@ namespace Serein.NodeFlow.Services /// public class FlowLibraryService { + /// + /// 构造函数,初始化流程依赖 + /// + /// public FlowLibraryService(IFlowEnvironment flowEnvironment) { this.flowEnvironment = flowEnvironment; @@ -35,10 +39,12 @@ namespace Serein.NodeFlow.Services /// 每个类库下面至少需要有“Serein.Library.dll”类库依赖 /// /// + /// /// 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 /// /// 程序集名称 /// 方法名称 - /// 返回的方法描述 + /// 返回的方法描述 /// 是否获取成功 public bool TryGetMethodInfo(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodInfo methodInfo) { diff --git a/NodeFlow/Services/FlowModelService.cs b/NodeFlow/Services/FlowModelService.cs index 320615f..1b82269 100644 --- a/NodeFlow/Services/FlowModelService.cs +++ b/NodeFlow/Services/FlowModelService.cs @@ -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; + /// + /// 流程模型服务构造函数 + /// + /// + /// public FlowModelService(IFlowEnvironment environment, FlowLibraryService flowLibraryService) { this.environment = environment; @@ -35,72 +41,156 @@ namespace Serein.NodeFlow.Services /// private List FlipflopNodes { get; } = []; + /// + /// 获取节点模型 + /// + /// + /// public IFlowNode? GetNodeModel(string guid) { NodeModels.TryGetValue(guid, out var nodeModel); return nodeModel; } + /// + /// 获取画布模型 + /// + /// + /// public FlowCanvasDetails? GetCanvasModel(string guid) { FlowCanvass.TryGetValue(guid, out var nodeModel); return nodeModel; } - public bool TryGetNodeModel(string guid, out IFlowNode flowNode) + /// + /// 尝试获取节点模型 + /// + /// + /// + /// + public bool TryGetNodeModel(string guid, [NotNullWhen(true)] out IFlowNode? flowNode) { return NodeModels.TryGetValue(guid, out flowNode!); } - public bool TryGetCanvasModel(string guid, out FlowCanvasDetails flowCanvas) + /// + /// 尝试获取画布模型 + /// + /// + /// + /// + 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; } - + /// + /// 检查是否包含节点模型 + /// + /// + /// public bool ContainsNodeModel(string guid) { return NodeModels.ContainsKey(guid); } + /// + /// 检查是否包含画布模型 + /// + /// + /// public bool ContainsCanvasModel(string guid) { return FlowCanvass.ContainsKey(guid); } + /// + /// 添加节点模型 + /// + /// + /// public bool AddNodeModel(IFlowNode flowNode) { ArgumentNullException.ThrowIfNull(flowNode); ArgumentNullException.ThrowIfNull(flowNode.Guid); return NodeModels.TryAdd(flowNode.Guid, flowNode); } + + /// + /// 添加画布模型 + /// + /// + /// public bool AddCanvasModel(FlowCanvasDetails flowCanvasDetails) { ArgumentNullException.ThrowIfNull(flowCanvasDetails); ArgumentNullException.ThrowIfNull(flowCanvasDetails.Guid); return FlowCanvass.TryAdd(flowCanvasDetails.Guid, flowCanvasDetails); } + + /// + /// 移除节点模型 + /// + /// + /// public bool RemoveNodeModel(IFlowNode flowNode) { ArgumentNullException.ThrowIfNull(flowNode.Guid); return NodeModels.Remove(flowNode.Guid); } + + /// + /// 移除画布模型 + /// + /// + /// public bool RemoveCanvasModel(FlowCanvasDetails flowCanvasDetails) { ArgumentNullException.ThrowIfNull(flowCanvasDetails.Guid); return FlowCanvass.Remove(flowCanvasDetails.Guid); } + /// + /// 获取所有节点模型 + /// + /// public List GetAllNodeModel() => [.. NodeModels.Values]; + + /// + /// 获取指定画布上的所有节点模型 + /// + /// + /// public List GetAllNodeModel(string canvasGuid) => NodeModels.Values.Where(x => x.CanvasDetails.Guid == canvasGuid).ToList(); + + /// + /// 获取所有画布模型 + /// + /// public List GetAllCanvasModel() => [.. FlowCanvass.Values]; + /// + /// 检查是否存在画布模型 + /// + /// public bool IsExsitCanvas() { return FlowCanvass.Count > 0; } + /// + /// 检查指定画布上是否存在节点模型 + /// + /// + /// public bool IsExsitNodeOnCanvas(string canvasGuid) { if (!FlowCanvass.TryGetValue(canvasGuid, out var flowCanvasDetails)) diff --git a/NodeFlow/Services/FlowOperationService.cs b/NodeFlow/Services/FlowOperationService.cs index 57865f7..f378a0c 100644 --- a/NodeFlow/Services/FlowOperationService.cs +++ b/NodeFlow/Services/FlowOperationService.cs @@ -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; diff --git a/NodeFlow/Tool/AssemblyLoader.cs b/NodeFlow/Tool/AssemblyLoader.cs index 6e68057..ca477cd 100644 --- a/NodeFlow/Tool/AssemblyLoader.cs +++ b/NodeFlow/Tool/AssemblyLoader.cs @@ -17,11 +17,22 @@ namespace Serein.NodeFlow.Tool private AssemblyLoadContext context; private Dictionary dicTypes = new Dictionary(); + /// + /// 程序集加载器构造函数 + /// + /// public AssemblyLoader(string basePath) { _basePath = basePath; } + + /// + /// 加载指定的类型 + /// + /// + /// + /// public Type Load(string dllFileName, string typeName) { context = new AssemblyLoadContext(dllFileName); diff --git a/NodeFlow/Tool/DynamicCompiler.cs b/NodeFlow/Tool/DynamicCompiler.cs index 0761b14..64645ac 100644 --- a/NodeFlow/Tool/DynamicCompiler.cs +++ b/NodeFlow/Tool/DynamicCompiler.cs @@ -12,6 +12,9 @@ namespace Serein.NodeFlow.Tool { private readonly HashSet _references = new HashSet(); + /// + /// 构造一个新的动态编译器实例。 + /// public DynamicCompiler() { // 默认添加当前 AppDomain 加载的所有程序集 @@ -110,10 +113,6 @@ namespace Serein.NodeFlow.Tool } - public void Save() - { - - } diff --git a/NodeFlow/Tool/LogTextWriter.cs b/NodeFlow/Tool/LogTextWriter.cs index de7e2bc..835672b 100644 --- a/NodeFlow/Tool/LogTextWriter.cs +++ b/NodeFlow/Tool/LogTextWriter.cs @@ -33,7 +33,11 @@ namespace Serein.NodeFlow.Tool /// public override Encoding Encoding => Encoding.UTF8; - + + /// + /// 重写Write方法,处理单个字符的写入 + /// + /// public override void Write(char value) { stringWriter.Write(value); @@ -43,6 +47,10 @@ namespace Serein.NodeFlow.Tool } } + /// + /// 重写Write方法,处理字符串的写入 + /// + /// public override void Write(string? value) { if (string.IsNullOrWhiteSpace(value)) return; @@ -53,6 +61,10 @@ namespace Serein.NodeFlow.Tool } } + /// + /// 重写WriteLine方法,处理字符串的换行写入 + /// + /// public override void WriteLine(string? value) { if (string.IsNullOrWhiteSpace(value)) return; diff --git a/NodeFlow/Tool/NativeDllHelper.cs b/NodeFlow/Tool/NativeDllHelper.cs index facbace..a65857f 100644 --- a/NodeFlow/Tool/NativeDllHelper.cs +++ b/NodeFlow/Tool/NativeDllHelper.cs @@ -103,8 +103,7 @@ namespace Serein.NodeFlow.Tool /// /// 加载Windows类库 /// - /// - /// 是否递归加载 + /// private static bool LoadWindowsLibrarie(string file) { IntPtr hModule = IntPtr.Zero; diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 681757f..9ec2a74 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -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; +/// +/// 节点方法描述帮助类 +/// public static class NodeMethodDetailsHelper { @@ -25,15 +29,16 @@ public static class NodeMethodDetailsHelper /// 方法所属的类型 /// 方法信息 /// 方法所属的程序集名称 + /// 传出的方法信息 /// 创建的方法描述,用来生成节点信息 /// 方法对应的Emit动态委托 /// 指示是否创建成功 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; } + + /// + /// 获取方法签名 + /// + /// + /// 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 - 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 ConvertorInstance =[]; @@ -231,6 +222,7 @@ public static class NodeMethodDetailsHelper } #endregion #region 存在自定义的转换器 +#if false else if (it.GetCustomAttribute() is BindConvertorAttribute attribute2 && attribute2 is not null) { paremType = attribute2.EnumType; @@ -262,6 +254,8 @@ public static class NodeMethodDetailsHelper return ed; } + +#endif #endregion #region 常规方法的获取参数 else diff --git a/NodeFlow/Tool/ObjDynamicCreateHelper.cs b/NodeFlow/Tool/ObjDynamicCreateHelper.cs index f6ce525..5db0b75 100644 --- a/NodeFlow/Tool/ObjDynamicCreateHelper.cs +++ b/NodeFlow/Tool/ObjDynamicCreateHelper.cs @@ -7,11 +7,20 @@ using System.Reflection.Emit; namespace Serein.NodeFlow.Tool { + /// + /// 动态创建对象的帮助类,支持根据属性字典创建对象并设置属性值。 + /// public class ObjDynamicCreateHelper { // 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式) static Dictionary typeCache = new Dictionary(); + /// + /// 根据属性字典和类型名称创建对象实例,并设置属性值。 + /// + /// + /// + /// public static object Resolve(Dictionary properties, string typeName) { var obj = CreateObjectWithProperties(properties, typeName); diff --git a/Serein.Extend.NewtonsoftJson/NewtonsoftJsonProvider.cs b/Serein.Extend.NewtonsoftJson/NewtonsoftJsonProvider.cs index 6f01731..9b4774a 100644 --- a/Serein.Extend.NewtonsoftJson/NewtonsoftJsonProvider.cs +++ b/Serein.Extend.NewtonsoftJson/NewtonsoftJsonProvider.cs @@ -16,13 +16,20 @@ namespace Serein.Extend.NewtonsoftJson /// public sealed class NewtonsoftJsonProvider : IJsonProvider { - private JsonSerializerSettings settings; + private JsonSerializerSettings? settings; + /// + /// 基于Newtonsoft.Json的JSON门户实现 + /// public NewtonsoftJsonProvider() { } + /// + /// 基于Newtonsoft.Json的JSON门户实现 + /// + /// public NewtonsoftJsonProvider(JsonType jsonType) { settings = jsonType switch @@ -48,39 +55,78 @@ namespace Serein.Extend.NewtonsoftJson this.settings = settings; } + /// + /// 反序列化JSON文本为指定类型的对象。 + /// + /// + /// + /// public T? Deserialize(string jsonText) { return JsonConvert.DeserializeObject(jsonText, settings); } + /// + /// 反序列化JSON文本为指定类型的对象。 + /// + /// + /// + /// public object? Deserialize(string jsonText, Type type) { return JsonConvert.DeserializeObject(jsonText, type, settings); } + /// + /// 序列化对象为JSON文本。 + /// + /// + /// public string Serialize(object obj) { return JsonConvert.SerializeObject(obj, settings); } + /// + /// 将JSON文本解析为IJsonToken对象。 + /// + /// + /// + public IJsonToken Parse(string json) { var token = JToken.Parse(json); return new NewtonsoftJsonToken(token); } - public IJsonToken CreateObject(IDictionary values = null) + /// + /// 创建一个新的JSON对象。 + /// + /// + /// + public IJsonToken CreateObject(IDictionary? values = null) { var jobj = values != null ? JObject.FromObject(values) : new JObject(); return new NewtonsoftJsonToken(jobj); } - public IJsonToken CreateArray(IEnumerable values = null) + /// + /// 创建一个新的JSON数组。 + /// + /// + /// + + public IJsonToken CreateArray(IEnumerable? values = null) { var jarr = values != null ? JArray.FromObject(values) : new JArray(); return new NewtonsoftJsonToken(jarr); } + /// + /// 将对象转换为IJsonToken。 + /// + /// + /// public IJsonToken FromObject(object obj) { var token = JToken.FromObject(obj); diff --git a/Serein.Extend.NewtonsoftJson/NewtonsoftJsonToken.cs b/Serein.Extend.NewtonsoftJson/NewtonsoftJsonToken.cs index daaf82b..345a12f 100644 --- a/Serein.Extend.NewtonsoftJson/NewtonsoftJsonToken.cs +++ b/Serein.Extend.NewtonsoftJson/NewtonsoftJsonToken.cs @@ -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; + /// + /// 使用JToken初始化一个新的NewtonsoftJsonToken实例。 + /// + /// + /// public NewtonsoftJsonToken(JToken token) { _token = token ?? throw new ArgumentNullException(nameof(token)); } - public bool TryGetValue(string name, out IJsonToken token) + /// + /// 尝试获取指定名称的属性值。 + /// + /// + /// + /// + 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(); @@ -50,10 +62,28 @@ namespace Serein.Extend.NewtonsoftJson throw new InvalidOperationException("当前Token不是数组类型。"); } + /// + /// 将当前JSON Token转换为指定类型的对象。 + /// + /// + /// +#pragma warning disable CS8603 // 可能返回 null 引用。 public T ToObject() => _token.ToObject(); +#pragma warning restore CS8603 // 可能返回 null 引用。 + /// + /// 将当前JSON Token转换为指定类型的对象。 + /// + /// + /// +#pragma warning disable CS8603 // 可能返回 null 引用。 public object ToObject(Type type) => _token.ToObject(type); +#pragma warning restore CS8603 // 可能返回 null 引用。 + /// + /// 返回当前JSON Token的字符串表示形式。 + /// + /// public override string ToString() => _token.ToString(); } } diff --git a/Serein.Extend.NewtonsoftJson/Serein.Extend.NewtonsoftJson.csproj b/Serein.Extend.NewtonsoftJson/Serein.Extend.NewtonsoftJson.csproj index 0dfec00..71f83eb 100644 --- a/Serein.Extend.NewtonsoftJson/Serein.Extend.NewtonsoftJson.csproj +++ b/Serein.Extend.NewtonsoftJson/Serein.Extend.NewtonsoftJson.csproj @@ -1,7 +1,7 @@  - net8.0;net462 + net8.0; enable latest enable @@ -16,6 +16,22 @@ + + 1701;1702;CS8766 + + + + 1701;1702;CS8766 + + + + 1701;1702;CS8766 + + + + 1701;1702;CS8766 + + diff --git a/Serein.Library.MyGenerator/Attribute.cs b/Serein.Library.MyGenerator/Attribute.cs index acec5db..f9e9dc9 100644 --- a/Serein.Library.MyGenerator/Attribute.cs +++ b/Serein.Library.MyGenerator/Attribute.cs @@ -40,20 +40,25 @@ namespace Serein.Library /// 标识一个类中的某些字段需要生成相应代码 /// [AttributeUsage(AttributeTargets.Class, Inherited = true)] - public sealed class NodePropertyAttribute : Attribute + public sealed class FlowDataPropertyAttribute : Attribute { /// /// 属性路径 /// CustomNode : 自定义节点 /// public NodeValuePath ValuePath = NodeValuePath.None; + + /// + /// 表示该类是否为节点实现类 + /// + public bool IsNodeImp = false; } /// /// 自动生成环境的属性 /// [AttributeUsage(AttributeTargets.Field, Inherited = true)] - public sealed class PropertyInfoAttribute : Attribute + public sealed class DataInfoAttribute : Attribute { /// /// 是否通知远程环境(如果在远程环境下) diff --git a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs b/Serein.Library.MyGenerator/FlowDataPropertyGenerator.cs similarity index 83% rename from Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs rename to Serein.Library.MyGenerator/FlowDataPropertyGenerator.cs index 218b484..09f8d8d 100644 --- a/Serein.Library.MyGenerator/ParameterDetailsPropertyGenerator.cs +++ b/Serein.Library.MyGenerator/FlowDataPropertyGenerator.cs @@ -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 { /// - /// 一个增量源生成器,用于为带有自定义 MyClassAttribute 特性的类中的字段生成带有自定义 set 行为的属性。 + /// 增量源生成器 /// [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(); /// /// 初始化生成器,定义需要执行的生成逻辑。 @@ -29,12 +30,16 @@ namespace Serein.Library.NodeGenerator /// 增量生成器的上下文,用于注册生成逻辑。 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 /// /// 类的语法树节点。 /// 类所在的命名空间。 + /// 。 /// 类的名称。 /// 生成的 C# 属性代码。 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(" /// "); - sb.AppendLine(" /// 略"); - sb.AppendLine(" /// 此事件为自动生成"); - sb.AppendLine(" /// "); - sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;"); + var isNodeImp = false; - sb.AppendLine(" protected bool SetProperty(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(" /// "); + sb.AppendLine(" /// 略"); + sb.AppendLine(" /// 此事件为自动生成"); + sb.AppendLine(" /// "); + sb.AppendLine(" public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged;"); - // 生成变量修改 + sb.AppendLine(" protected bool SetProperty(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(" /// "); //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(value); - } - } - else if (path.Equals(nameof(Guid), StringComparison.OrdinalIgnoreCase)) - { - if (typeof(string) == typeof(string)) - { - this.Guid = value; - } - else - { - this.Guid = ConvertHelper.ValueParse(value); - } - } - }*/ - - /// /// 获取类所在的命名空间。 /// @@ -347,21 +333,16 @@ namespace Serein.Library.NodeGenerator } - - private void SetterIsProtection() - { - - } - private void SetterNotIsProtection() - { - - } + } + /// + /// 扩展方法,用于处理 XML 文档注释中的 summary 标签 + /// public static class DocumentationCommentExtensions { @@ -431,10 +412,18 @@ namespace Serein.Library.NodeGenerator } } - + + /// + /// MyAttributeResolver + /// public static class MyAttributeResolver { + /// + /// 构建类的特性缓存信息 + /// + /// + /// public static Dictionary> BuildCacheOfClass(this INamedTypeSymbol classSymbol) { Dictionary> attributesOfClass = new Dictionary>(); @@ -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 } + /// + /// 检查类信息中是否存在指定的路径 + /// + /// + /// + /// public static bool ExitsPath(this Dictionary> 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; } diff --git a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj index 0579a29..5d42f45 100644 --- a/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj +++ b/Serein.Library.MyGenerator/Serein.Library.NodeGenerator.csproj @@ -2,8 +2,9 @@ netstandard2.0 - 1.2.1 - true + 1.2.3 + false + ..\.\.Output True SereinFow @@ -13,6 +14,7 @@ MIT True true + true diff --git a/Serein.Proto.Modbus/ModbusRequest.cs b/Serein.Proto.Modbus/ModbusRequest.cs index 9b79e74..f315f0a 100644 --- a/Serein.Proto.Modbus/ModbusRequest.cs +++ b/Serein.Proto.Modbus/ModbusRequest.cs @@ -10,12 +10,12 @@ /// /// PDU (Protocol Data Unit) 数据,不包括从站地址和CRC /// - public byte[] PDU { get; set; } + public byte[]? PDU { get; set; } /// /// 异步任务完成源,用于等待响应 /// - public TaskCompletionSource Completion { get; set; } + public TaskCompletionSource? Completion { get; set; } } } diff --git a/Serein.Proto.Modbus/ModbusRtuClient.cs b/Serein.Proto.Modbus/ModbusRtuClient.cs index 85025de..a304751 100644 --- a/Serein.Proto.Modbus/ModbusRtuClient.cs +++ b/Serein.Proto.Modbus/ModbusRtuClient.cs @@ -24,7 +24,10 @@ namespace Serein.Proto.Modbus private readonly CancellationTokenSource _cts = new(); + +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 public ModbusRtuClient(string portName, int baudRate = 9600, Parity parity = Parity.None, int dataBits = 8, StopBits stopBits = StopBits.One, byte slaveId = 1) +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 { _slaveId = slaveId; _serialPort = new SerialPort(portName, baudRate, parity, dataBits, stopBits) diff --git a/Serein.Proto.Modbus/ModbusTcpClient.cs b/Serein.Proto.Modbus/ModbusTcpClient.cs index e162ff7..224c52b 100644 --- a/Serein.Proto.Modbus/ModbusTcpClient.cs +++ b/Serein.Proto.Modbus/ModbusTcpClient.cs @@ -42,7 +42,9 @@ namespace Serein.Proto.Modbus /// private int _transactionId = 0; +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 public ModbusTcpClient(string host, int port = 502) +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 { _tcpClient = new TcpClient(); _tcpClient.Connect(host, port); @@ -271,6 +273,11 @@ namespace Serein.Proto.Modbus while (_tcpClient.Connected) { var request = await _channel.Reader.ReadAsync(); + if (request.PDU is null) + { + request.Completion?.TrySetCanceled(); + continue; + } byte[] packet = BuildPacket(request.TransactionId, 0x01, (byte)request.FunctionCode, request.PDU); OnTx?.Invoke(packet); // 触发发送日志 await _stream.WriteAsync(packet, 0, packet.Length); diff --git a/Serein.Proto.Modbus/ModbusUdpClient.cs b/Serein.Proto.Modbus/ModbusUdpClient.cs index 9b0deb1..9d4218a 100644 --- a/Serein.Proto.Modbus/ModbusUdpClient.cs +++ b/Serein.Proto.Modbus/ModbusUdpClient.cs @@ -24,7 +24,9 @@ namespace Serein.Proto.Modbus private readonly ConcurrentDictionary> _pendingRequests = new(); private int _transactionId = 0; +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 public ModbusUdpClient(string host, int port = 502) +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 { _remoteEndPoint = new IPEndPoint(IPAddress.Parse(host), port); _udpClient = new UdpClient(); @@ -153,11 +155,20 @@ namespace Serein.Proto.Modbus return tcs.Task; } + /// + /// 处理发送队列的异步方法 + /// + /// private async Task ProcessQueueAsync() { while (true) { var request = await _channel.Reader.ReadAsync(); + if(request.PDU is null) + { + request.Completion?.TrySetCanceled(); + continue; + } byte[] packet = BuildPacket(request.TransactionId, 0x01, (byte)request.FunctionCode, request.PDU); OnTx?.Invoke(packet); await _udpClient.SendAsync(packet, packet.Length); diff --git a/Serein.Proto.WebSocket/Attributes/AutoSocketModuleAttribute.cs b/Serein.Proto.WebSocket/Attributes/AutoSocketModuleAttribute.cs index 7015a1b..43c9734 100644 --- a/Serein.Proto.WebSocket/Attributes/AutoSocketModuleAttribute.cs +++ b/Serein.Proto.WebSocket/Attributes/AutoSocketModuleAttribute.cs @@ -21,15 +21,15 @@ /// /// 业务标识 /// - public string ThemeKey; + public string ThemeKey = string.Empty; /// /// 数据标识 /// - public string DataKey; + public string DataKey = string.Empty; /// /// ID标识 /// - public string MsgIdKey; + public string MsgIdKey = string.Empty; /// /// 指示应答数据回复方法返回值 diff --git a/Serein.Proto.WebSocket/Handle/HandleConfiguration.cs b/Serein.Proto.WebSocket/Handle/HandleConfiguration.cs index fdc279b..1b9c3d8 100644 --- a/Serein.Proto.WebSocket/Handle/HandleConfiguration.cs +++ b/Serein.Proto.WebSocket/Handle/HandleConfiguration.cs @@ -13,17 +13,17 @@ namespace Serein.Proto.WebSocket.Handle /// /// Emit委托 /// - public DelegateDetails DelegateDetails { get; set; } + public DelegateDetails? DelegateDetails { get; set; } /// /// 未捕获的异常跟踪 /// - public Action> OnExceptionTracking { get; set; } + public Action>? OnExceptionTracking { get; set; } /// /// 所使用的实例 /// - public Func InstanceFactory { get; set; } + public Func? InstanceFactory { get; set; } /// /// 是否需要返回 @@ -38,32 +38,32 @@ namespace Serein.Proto.WebSocket.Handle /// /// 是否使用Data整体内容作为入参参数 /// - public bool[] UseData { get; set; } + public bool[] UseData { get; set; } = []; /// /// 是否使用Request整体内容作为入参参数 /// - public bool[] UseRequest { get; set; } + public bool[] UseRequest { get; set; } = []; /// /// 是否使用消息ID作为入参参数 /// - public bool[] UseMsgId { get; set; } + public bool[] UseMsgId { get; set; } = []; /// /// 参数名称 /// - public string[] ParameterName { get; set; } + public string[] ParameterName { get; set; } = []; /// /// 参数类型 /// - public Type[] ParameterType { get; set; } + public Type[] ParameterType { get; set; } = []; /// /// 是否检查变量为空 /// - public bool[] IsCheckArgNotNull { get; set; } + public bool[] IsCheckArgNotNull { get; set; } = []; } diff --git a/Serein.Proto.WebSocket/Handle/WebSocketHandleModule.cs b/Serein.Proto.WebSocket/Handle/WebSocketHandleModule.cs index af4274c..bd0fd29 100644 --- a/Serein.Proto.WebSocket/Handle/WebSocketHandleModule.cs +++ b/Serein.Proto.WebSocket/Handle/WebSocketHandleModule.cs @@ -86,33 +86,62 @@ namespace Serein.Proto.WebSocket.Handle public async Task HandleAsync(WebSocketMsgContext context) { var jsonObject = context.MsgRequest; // 获取到消息 - string theme = jsonObject.GetValue(moduleConfig.ThemeJsonKey)?.ToString(); - if (!MyHandleConfigs.TryGetValue(theme, out var handldConfig)) + + if (jsonObject is null) { + // SereinEnv.WriteLine(InfoType.WARN, "没有获取到消息"); + return; // 没有获取到消息 + } + + // 验证主题 + if (!jsonObject.TryGetValue(moduleConfig.ThemeJsonKey, out var themeToken) + || themeToken.ToString() is not string theme + || !MyHandleConfigs.TryGetValue(theme, out var handldConfig)) + { + // SereinEnv.WriteLine(InfoType.WARN, $"{theme} 主题不存在"); + return; + } + + // 验证消息ID + if (!jsonObject.TryGetValue(moduleConfig.MsgIdJsonKey, out var msgIdToken) + || msgIdToken.ToString() is not string msgId) + { + // SereinEnv.WriteLine(InfoType.WARN, $"[{msgId}]{theme} 没有消息Id"); + return; + } + + // 验证消息ID是否重复 + if (!_myMsgIdHash.Add(msgId)) + { + // SereinEnv.WriteLine(InfoType.WARN, $"[{msgId}]{theme} 消息重复"); + return; // 消息重复 + } + + // 验证数据 + if (!jsonObject.TryGetValue(moduleConfig.DataJsonKey, out var dataToken)) + { + // SereinEnv.WriteLine(InfoType.WARN, $"[{msgId}]{theme} 消息重复"); return; // 没有主题 } - context.MsgTheme = theme; // 添加主题 - string msgId = jsonObject.GetValue(moduleConfig.MsgIdJsonKey)?.ToString(); - if (_myMsgIdHash.Contains(msgId)) - { - SereinEnv.WriteLine(InfoType.WARN, $"[{msgId}]{theme} 消息重复"); - return; - } - context.MsgId = msgId; // 添加 ID - _myMsgIdHash.Add(msgId); + context.MsgTheme = theme; // 添加主题 + context.MsgId = msgId; // 添加 ID + context.MsgData = dataToken; // 添加消息 + context.MsgRequest = jsonObject; // 添加原始消息 try { - var dataObj = jsonObject.GetValue(moduleConfig.DataJsonKey); - context.MsgData = dataObj; // 添加消息 if (TryGetParameters(handldConfig, context, out var args)) { - var result = await HandleAsync(handldConfig, args); + var result = await HandleAsync(handldConfig, args); if (handldConfig.IsReturnValue) { await context.RepliedAsync(moduleConfig, context, result); } } + else + { + SereinEnv.WriteLine(InfoType.WARN, $"[{msgId}]{theme} 参数获取失败"); + } } catch (Exception ex) { @@ -131,9 +160,13 @@ namespace Serein.Proto.WebSocket.Handle /// /// /// - public static async Task HandleAsync(HandleConfiguration config, object[] args) + public static async Task HandleAsync(HandleConfiguration config, object?[] args) { - var instance = config.InstanceFactory.Invoke(); + if (config.DelegateDetails is null) + { + throw new InvalidOperationException("DelegateDetails 为 null, 无法进行调用."); + } + var instance = config.InstanceFactory?.Invoke(); var result = await config.DelegateDetails.InvokeAsync(instance, args); return result; } @@ -146,7 +179,7 @@ namespace Serein.Proto.WebSocket.Handle /// 处理上下文 /// 返回的入参参数 /// - internal static bool TryGetParameters(HandleConfiguration config, WebSocketMsgContext context, out object[] args) + internal static bool TryGetParameters(HandleConfiguration config, WebSocketMsgContext context, out object?[] args) { args = new object[config.ParameterType.Length]; bool isCanInvoke = true; ; // 表示是否可以调用方法 @@ -165,20 +198,20 @@ namespace Serein.Proto.WebSocket.Handle #region DATA JSON数据 else if (config.UseRequest[i]) { - args[i] = context.MsgRequest.ToObject(type); + args[i] = context.MsgRequest?.ToObject(type); } #endregion #region DATA JSON数据 else if (config.UseData[i]) { - args[i] = context.MsgData.ToObject(type); + args[i] = context.MsgData?.ToObject(type); } #endregion #region 值类型参数 else if (type.IsValueType) { - var jsonValue = context.MsgData.GetValue(argName); - if (!(jsonValue is null)) + var jsonValue = context.MsgData?.GetValue(argName); + if (jsonValue is not null) { args[i] = jsonValue.ToObject(type); } @@ -200,14 +233,14 @@ namespace Serein.Proto.WebSocket.Handle #region 引用类型参数 else if (type.IsClass) { - var jsonValue = context.MsgData.GetValue(argName); + var jsonValue = context.MsgData?.GetValue(argName); if (!(jsonValue is null)) { args[i] = jsonValue.ToObject(type); } else { - if (config.ArgNotNull && !config.IsCheckArgNotNull[i]) + if (!config.ArgNotNull && !config.IsCheckArgNotNull[i]) { args[i] = null; // 引用类型返回null diff --git a/Serein.Proto.WebSocket/Handle/WebSocketHandleModuleConfig.cs b/Serein.Proto.WebSocket/Handle/WebSocketHandleModuleConfig.cs index 9bc574b..719a3b2 100644 --- a/Serein.Proto.WebSocket/Handle/WebSocketHandleModuleConfig.cs +++ b/Serein.Proto.WebSocket/Handle/WebSocketHandleModuleConfig.cs @@ -8,15 +8,15 @@ /// /// 有关消息ID的 Json Key /// - public string MsgIdJsonKey { get; set; } + public string MsgIdJsonKey { get; set; } = string.Empty; /// /// 有关消息主题的 Json Key /// - public string ThemeJsonKey { get; set; } + public string ThemeJsonKey { get; set; } = string.Empty; /// /// 有关数据的 Json Key /// - public string DataJsonKey { get; set; } + public string DataJsonKey { get; set; } = string.Empty; /// /// 使用怎么样的数据 /// diff --git a/Serein.Proto.WebSocket/Handle/WebSocketMsgContext.cs b/Serein.Proto.WebSocket/Handle/WebSocketMsgContext.cs index b6dfba2..acb8407 100644 --- a/Serein.Proto.WebSocket/Handle/WebSocketMsgContext.cs +++ b/Serein.Proto.WebSocket/Handle/WebSocketMsgContext.cs @@ -19,8 +19,8 @@ namespace Serein.Proto.WebSocket.Handle public void Dispose() { MsgRequest = null; - MsgTheme = null; - MsgId = null; + MsgTheme = string.Empty; + MsgId = string.Empty; MsgData = null; MsgData = null; _sendAsync = null; @@ -42,25 +42,25 @@ namespace Serein.Proto.WebSocket.Handle /// /// 消息本体(IJsonToken) /// - public IJsonToken MsgRequest { get; set; } + public IJsonToken? MsgRequest { get; set; } /// /// 此次消息请求的主题 /// - public string MsgTheme { get; set; } + public string MsgTheme { get; set; } = string.Empty; /// /// 此次消息附带的ID /// - public string MsgId { get; set; } + public string MsgId { get; set; } = string.Empty; /// /// 此次消息的数据 /// - public IJsonToken MsgData { get; set; } + public IJsonToken? MsgData { get; set; } - private Func _sendAsync; + private Func? _sendAsync; /// /// 发送消息 @@ -69,6 +69,7 @@ namespace Serein.Proto.WebSocket.Handle /// public async Task SendAsync(string msg) { + if (_sendAsync is null) return; await _sendAsync.Invoke(msg); } diff --git a/Serein.Proto.WebSocket/Handle/WebSocketMsgHandleHelper.cs b/Serein.Proto.WebSocket/Handle/WebSocketMsgHandleHelper.cs index 89be00a..cf9abb5 100644 --- a/Serein.Proto.WebSocket/Handle/WebSocketMsgHandleHelper.cs +++ b/Serein.Proto.WebSocket/Handle/WebSocketMsgHandleHelper.cs @@ -17,11 +17,10 @@ namespace Serein.Proto.WebSocket.Handle public ConcurrentDictionary<(string, string), WebSocketHandleModule> MyHandleModuleDict = new ConcurrentDictionary<(string, string), WebSocketHandleModule>(); - private Action> _onExceptionTracking; /// /// 异常跟踪 /// - public event Action> OnExceptionTracking; + public Action>? OnExceptionTracking; /// /// 添加消息处理与异常处理 @@ -92,12 +91,12 @@ namespace Serein.Proto.WebSocket.Handle var handleModule = AddMyHandleModule(moduleConfig); var configs = type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .Select(methodInfo => + .Select(methodInfo => { var methodsAttribute = methodInfo.GetCustomAttribute(); if (methodsAttribute is null) { - return null; + return default; } else { @@ -135,7 +134,7 @@ namespace Serein.Proto.WebSocket.Handle config.InstanceFactory = instanceFactory; // 调用emit委托时的实例 config.OnExceptionTracking = onExceptionTracking; // 异常追踪 config.ParameterType = parameterInfos.Select(t => t.ParameterType).ToArray(); // 入参参数类型 - config.ParameterName = parameterInfos.Select(t => t.Name).ToArray(); // 入参参数名称 + config.ParameterName = parameterInfos.Select(t => $"{t.Name}").ToArray(); // 入参参数名称 config.UseRequest = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否使用整体data数据 config.UseData = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否使用整体data数据 config.UseMsgId = parameterInfos.Select(p => p.GetCustomAttribute() != null).ToArray(); // 是否使用消息ID @@ -179,7 +178,7 @@ namespace Serein.Proto.WebSocket.Handle SereinEnv.WriteLine(InfoType.INFO, $"theme key, data key : {themeKey}, {dataKey}"); foreach (var config in configs) { - SereinEnv.WriteLine(InfoType.INFO, $"theme value : {config.ThemeValue} "); + SereinEnv.WriteLine(InfoType.INFO, $"theme value : {config!.ThemeValue} "); var result = handleModule.AddHandleConfigs(config); } diff --git a/Serein.Proto.WebSocket/TestExtension.cs b/Serein.Proto.WebSocket/TestExtension.cs index a3bbc91..a12effc 100644 --- a/Serein.Proto.WebSocket/TestExtension.cs +++ b/Serein.Proto.WebSocket/TestExtension.cs @@ -42,7 +42,7 @@ namespace Serein.Proto.WebSocket { return await _msgChannel.Reader.ReadAsync(); } - return null; // 若通道关闭,则返回null + return string.Empty; // 若通道关闭,则返回null } /// diff --git a/Serein.Proto.WebSocket/WebSocketServer.cs b/Serein.Proto.WebSocket/WebSocketServer.cs index bd68713..b06b4fa 100644 --- a/Serein.Proto.WebSocket/WebSocketServer.cs +++ b/Serein.Proto.WebSocket/WebSocketServer.cs @@ -52,13 +52,17 @@ namespace Serein.Proto.WebSocket /// public async Task HandleAuthorized(string message) { + if (InspectionAuthorizedFunc is null) + { + return false; // 如果没有授权方法,则默认解决 + } await semaphoreSlim.WaitAsync(1); bool isAuthorized = false; IJsonToken json = JsonHelper.Parse(message); if(json.TryGetValue(TokenKey,out var token)) { // 交给之前定义的授权方法进行判断 - isAuthorized = await InspectionAuthorizedFunc?.Invoke(token); + isAuthorized = await InspectionAuthorizedFunc.Invoke(token); } else { @@ -81,7 +85,7 @@ namespace Serein.Proto.WebSocket /// public WebSocketMsgHandleHelper MsgHandleHelper { get; } = new WebSocketMsgHandleHelper(); - private HttpListener listener; + private HttpListener? listener; /// /// 创建无须授权验证的WebSocket服务端 @@ -110,9 +114,11 @@ namespace Serein.Proto.WebSocket /// 授权 /// public ConcurrentDictionary AuthorizedClients; - private readonly string TokenKey; + private readonly string TokenKey = string.Empty; private readonly Func> InspectionAuthorizedFunc; private bool IsCheckToken = false; + + /// /// 进行监听服务 /// @@ -138,13 +144,14 @@ namespace Serein.Proto.WebSocket try { var context = await listener.GetContextAsync(); - string clientPoint = context.Request.RemoteEndPoint?.ToString(); + string clientPoint = context.Request.RemoteEndPoint.ToString(); await Console.Out.WriteLineAsync($"新的连接加入:{clientPoint}"); if (context.Request.IsWebSocketRequest) { - WebSocketAuthorizedHelper authorizedHelper = null; +#error "需要重写 WebSocket 服务" + WebSocketAuthorizedHelper? authorizedHelper = null; if (IsCheckToken) { if (AuthorizedClients.TryAdd(clientPoint, new WebSocketAuthorizedHelper(clientPoint, TokenKey, InspectionAuthorizedFunc))) @@ -154,7 +161,7 @@ namespace Serein.Proto.WebSocket } var webSocketContext = await context.AcceptWebSocketAsync(null); //新连接 - _ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper); // 处理消息 + _ = HandleWebSocketAsync(webSocketContext.WebSocket, authorizedHelper!); // 处理消息 } } catch (Exception ex) diff --git a/Serein.Script/IScriptInvokeContext.cs b/Serein.Script/IScriptInvokeContext.cs index ced9287..a5dfa3c 100644 --- a/Serein.Script/IScriptInvokeContext.cs +++ b/Serein.Script/IScriptInvokeContext.cs @@ -32,7 +32,7 @@ namespace Serein.Script /// /// /// - object GetVarValue(string varName); + object? GetVarValue(string varName); /// /// 设置变量的值 diff --git a/Serein.Script/Node/ASTNode.cs b/Serein.Script/Node/ASTNode.cs index c184bea..e3aa244 100644 --- a/Serein.Script/Node/ASTNode.cs +++ b/Serein.Script/Node/ASTNode.cs @@ -8,7 +8,7 @@ namespace Serein.Script.Node { public abstract class ASTNode { - public string Code { get; private set; } + public string Code { get; private set; } = string.Empty; public int Row { get; private set; } public int StartIndex { get; private set; } public int Length { get; private set; } diff --git a/Serein.Script/Node/FlowControl/ReturnNode.cs b/Serein.Script/Node/FlowControl/ReturnNode.cs index 5031af4..f274545 100644 --- a/Serein.Script/Node/FlowControl/ReturnNode.cs +++ b/Serein.Script/Node/FlowControl/ReturnNode.cs @@ -14,7 +14,7 @@ namespace Serein.Script.Node.FlowControl /// /// 返回值来源 /// - public ASTNode Value { get; } + public ASTNode? Value { get; } public ReturnNode(ASTNode returnNode) { diff --git a/Serein.Script/ScriptInvokeContext.cs b/Serein.Script/ScriptInvokeContext.cs index ee62bb0..8d3b71c 100644 --- a/Serein.Script/ScriptInvokeContext.cs +++ b/Serein.Script/ScriptInvokeContext.cs @@ -20,7 +20,9 @@ namespace Serein.Script { } - public IFlowContext FlowContext{ get; } +#pragma warning disable CS8766 // 返回类型中引用类型的为 Null 性与隐式实现的成员不匹配(可能是由于为 Null 性特性)。 + public IFlowContext? FlowContext{ get; } +#pragma warning restore CS8766 // 返回类型中引用类型的为 Null 性与隐式实现的成员不匹配(可能是由于为 Null 性特性)。 /// /// 定义的变量 @@ -48,13 +50,24 @@ namespace Serein.Script public bool IsNeedReturn { get; set; } - object IScriptInvokeContext.GetVarValue(string varName) + /// + /// 获取变量的值 + /// + /// + /// + object? IScriptInvokeContext.GetVarValue(string varName) { _variables.TryGetValue(varName, out var value); return value; } + /// + /// 设置变量的值 + /// + /// + /// + /// bool IScriptInvokeContext.SetVarValue(string varName, object? value) { if (!_variables.TryAdd(varName, value)) diff --git a/Serein.Script/SereinScriptExtension.cs b/Serein.Script/SereinScriptExtension.cs index 3693091..2aeb9b6 100644 --- a/Serein.Script/SereinScriptExtension.cs +++ b/Serein.Script/SereinScriptExtension.cs @@ -16,7 +16,7 @@ namespace Serein.Script /// 字符串构建器本身 public static StringBuilder AppendCode(this StringBuilder sb, int retractCount = 0, - string code = null, + string? code = null, bool isWrapping = true) { if (!string.IsNullOrWhiteSpace(code)) diff --git a/Serein.Script/SereinScriptILCompiler.cs b/Serein.Script/SereinScriptILCompiler.cs index 9e8b050..48094ec 100644 --- a/Serein.Script/SereinScriptILCompiler.cs +++ b/Serein.Script/SereinScriptILCompiler.cs @@ -3,6 +3,7 @@ using Serein.Script.Node; using Serein.Script.Node.FlowControl; using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -15,15 +16,32 @@ using System.Xml.Linq; namespace Serein.Script { -/* - 暂未想到如何编译出具备 await / async 功能的IL代码,暂时放弃 - */ + /* + 暂未想到如何编译出具备 await / async 功能的IL代码,暂时放弃 + */ + + /// + /// IL 代码生成结果 + /// internal record ILResult { - public Type ReturnType { get; set; } + /// + /// 返回类型 + /// + public Type? ReturnType { get; set; } + + /// + /// 临时变量,可用于缓存值,避免重复计算 + /// public LocalBuilder? TempVar { get; set; } // 可用于缓存 + + /// + /// 发射 IL 代码的委托,接受 ILGenerator 参数 + /// +#pragma warning disable CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 public Action Emit { get; set; } // 用于推入值的代码 +#pragma warning restore CS8618 // 在退出构造函数时,不可为 null 的字段必须包含非 null 值。请考虑添加 "required" 修饰符或声明为可为 null。 } @@ -62,6 +80,7 @@ namespace Serein.Script /// Dictionary _ilResults = new Dictionary(); + private Label _methodExit; public SereinScriptILCompiler(Dictionary symbolInfos) @@ -72,7 +91,7 @@ namespace Serein.Script /// /// 是否调用了异步方法 /// - private bool _isUseAwait = false; + //private bool _isUseAwait = false; private Dictionary _parameterIndexes = new Dictionary(); @@ -1065,12 +1084,12 @@ namespace Serein.Script // 如果方法签名需要返回值,这里可放置默认 return 语句或用局部变量存储返回值 }*/ - private static bool IsGenericTask(Type returnType, out Type taskResult) + private static bool IsGenericTask(Type returnType,[NotNullWhen(true)] out Type? taskResult) { // 判断是否为 Task 类型或泛型 Task if (returnType == typeof(Task)) { - taskResult = null; + taskResult = typeof(void); return true; } else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) diff --git a/Serein.Script/SereinScriptLexer.cs b/Serein.Script/SereinScriptLexer.cs index 92a6efb..619701b 100644 --- a/Serein.Script/SereinScriptLexer.cs +++ b/Serein.Script/SereinScriptLexer.cs @@ -286,7 +286,7 @@ { var start = _index; bool hasDot = false; - bool hasSuffix = false; + //bool hasSuffix = false; while (_index < _input.Length) { @@ -303,7 +303,7 @@ } else if (ch is 'f' or 'F' or 'd' or 'D' or 'l' or 'L') { - hasSuffix = true; + //hasSuffix = true; _index++; break; // 后缀后应结束 } diff --git a/Serein.Script/SereinScriptTypeAnalysis.cs b/Serein.Script/SereinScriptTypeAnalysis.cs index 8292865..90af454 100644 --- a/Serein.Script/SereinScriptTypeAnalysis.cs +++ b/Serein.Script/SereinScriptTypeAnalysis.cs @@ -378,7 +378,7 @@ namespace Serein.Script Type argType = types[index]; NodeSymbolInfos[argNode] = argType; } - var isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult); + var isAsync = EmitHelper.IsGenericTask(methodInfo.ReturnType, out var taskResult); var methodReturnType = isAsync ? taskResult : methodInfo.ReturnType; AsyncMethods[memberFunctionCallNode] = isAsync; NodeSymbolInfos[memberFunctionCallNode.Object] = objectType; @@ -402,7 +402,7 @@ namespace Serein.Script Type argType = types[index]; NodeSymbolInfos[argNode] = argType; } - var isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult); + var isAsync = EmitHelper.IsGenericTask(methodInfo.ReturnType, out var taskResult); var methodReturnType = isAsync ? taskResult : methodInfo.ReturnType; AsyncMethods[functionCallNode] = isAsync; NodeSymbolInfos[functionCallNode] = methodReturnType; @@ -583,43 +583,15 @@ namespace Serein.Script { return resultType; } - try + resultType = Type.GetType(typeName); // 从命名空间查询类型 + if (resultType != null) { - resultType = Type.GetType(typeName); // 从命名空间查询类型 - if (resultType != null) - { - return resultType; - } - throw new InvalidOperationException($"无法匹配类型 {typeName}"); - } - catch (Exception ex) - { - throw; + return resultType; } + throw new InvalidOperationException($"无法匹配类型 {typeName}"); } - private static bool IsGenericTask(Type returnType, out Type taskResult) - { - // 判断是否为 Task 类型或泛型 Task - if (returnType == typeof(Task)) - { - taskResult = typeof(void); - return true; - } - else if (returnType.IsGenericType && returnType.GetGenericTypeDefinition() == typeof(Task<>)) - { - // 获取泛型参数类型 - Type genericArgument = returnType.GetGenericArguments()[0]; - taskResult = genericArgument; - return true; - } - else - { - taskResult = null; - return false; - - } - } + /// /// 获取某个集合类型支持的索引参数类型 diff --git a/SereinFlow.sln b/SereinFlow.sln index 62f0496..cd1e0fa 100644 --- a/SereinFlow.sln +++ b/SereinFlow.sln @@ -20,10 +20,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Library.NodeGenerato EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Script", "Serein.Script\Serein.Script.csproj", "{D14BC18C-3D69-49FA-BEEA-A9AA570C7469}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Workbench.Avalonia", "Serein.Workbench.Avalonia\Serein.Workbench.Avalonia.csproj", "{3E11F86C-5914-4998-81DC-6688E5E1A59B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serein.Workbench.Avalonia.Desktop", "Serein.Workbench.Avalonia.Desktop\Serein.Workbench.Avalonia.Desktop.csproj", "{D46C9E9E-9994-45E6-8084-A8A9497B1DC2}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.CollaborationSync", "Serein.CollaborationSync\Serein.CollaborationSync.csproj", "{913AAB34-7383-4F9D-A7DF-A5E6191DDB3D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serein.Extend.NewtonsoftJson", "Serein.Extend.NewtonsoftJson\Serein.Extend.NewtonsoftJson.csproj", "{1961CF3C-29FC-4850-91DE-1DD571D9514D}" @@ -62,14 +58,6 @@ Global {D14BC18C-3D69-49FA-BEEA-A9AA570C7469}.Debug|Any CPU.Build.0 = Debug|Any CPU {D14BC18C-3D69-49FA-BEEA-A9AA570C7469}.Release|Any CPU.ActiveCfg = Release|Any CPU {D14BC18C-3D69-49FA-BEEA-A9AA570C7469}.Release|Any CPU.Build.0 = Release|Any CPU - {3E11F86C-5914-4998-81DC-6688E5E1A59B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3E11F86C-5914-4998-81DC-6688E5E1A59B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3E11F86C-5914-4998-81DC-6688E5E1A59B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3E11F86C-5914-4998-81DC-6688E5E1A59B}.Release|Any CPU.Build.0 = Release|Any CPU - {D46C9E9E-9994-45E6-8084-A8A9497B1DC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D46C9E9E-9994-45E6-8084-A8A9497B1DC2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D46C9E9E-9994-45E6-8084-A8A9497B1DC2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D46C9E9E-9994-45E6-8084-A8A9497B1DC2}.Release|Any CPU.Build.0 = Release|Any CPU {913AAB34-7383-4F9D-A7DF-A5E6191DDB3D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {913AAB34-7383-4F9D-A7DF-A5E6191DDB3D}.Debug|Any CPU.Build.0 = Debug|Any CPU {913AAB34-7383-4F9D-A7DF-A5E6191DDB3D}.Release|Any CPU.ActiveCfg = Release|Any CPU diff --git a/Workbench/Api/INodeJunction.cs b/Workbench/Api/INodeJunction.cs index 51a5a48..a5c6f9b 100644 --- a/Workbench/Api/INodeJunction.cs +++ b/Workbench/Api/INodeJunction.cs @@ -10,10 +10,10 @@ namespace Serein.Workbench.Avalonia.Api - /// + /* /// /// 约束一个节点应该有哪些控制点 /// - /*public interface INodeJunction + public interface INodeJunction { /// /// 方法执行入口控制点 diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs index 4452196..56c5835 100644 --- a/Workbench/App.xaml.cs +++ b/Workbench/App.xaml.cs @@ -34,12 +34,12 @@ namespace Serein.Workbench /// public static UIContextOperation UIContextOperation => App.GetService() ?? throw new NullReferenceException(); - public static T GetService() where T : class + internal static T GetService() where T : class { return ServiceProvider?.GetService() ?? throw new NullReferenceException(); } - - public App() + + internal App() { var collection = new ServiceCollection(); collection.AddWorkbenchServices(); @@ -84,20 +84,19 @@ namespace Serein.Workbench obj.Id = 114514;*/ - if (1 == 11) - { +#if false var projectService = App.GetService(); await Task.Delay(500); string filePath; filePath = @"F:\TempFile\flow\temp2\project.dnf"; projectService.LoadLocalProject(filePath); - } +#endif } #endif - private async void Application_Startup(object sender, StartupEventArgs e) + private void Application_Startup(object sender, StartupEventArgs e) { var projectService = App.GetService(); if (e.Args.Length == 1) diff --git a/Workbench/Converters/BoolToVisibilityConverter.cs b/Workbench/Converters/BoolToVisibilityConverter.cs index 4e8b68f..ffb2ffa 100644 --- a/Workbench/Converters/BoolToVisibilityConverter.cs +++ b/Workbench/Converters/BoolToVisibilityConverter.cs @@ -10,7 +10,7 @@ using System.Diagnostics; namespace Serein.Workbench.Converters { - public class BoolToVisibilityConverter : IValueConverter + internal class BoolToVisibilityConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { diff --git a/Workbench/Converters/CountToVisibilityConverter.cs b/Workbench/Converters/CountToVisibilityConverter.cs index c264193..4a3d7ce 100644 --- a/Workbench/Converters/CountToVisibilityConverter.cs +++ b/Workbench/Converters/CountToVisibilityConverter.cs @@ -10,7 +10,7 @@ using System.Windows; namespace Serein.Workbench.Converters { - public class CountToVisibilityConverter : IValueConverter + internal class CountToVisibilityConverter : IValueConverter { public bool Inverse { get; set; } = false; // 可选:反转逻辑 diff --git a/Workbench/Converters/EnumToBooleanConverter.cs b/Workbench/Converters/EnumToBooleanConverter.cs index a55dae0..d553c89 100644 --- a/Workbench/Converters/EnumToBooleanConverter.cs +++ b/Workbench/Converters/EnumToBooleanConverter.cs @@ -8,14 +8,17 @@ using System.Windows.Data; namespace Serein.Workbench.Converters { - public class EnumToBooleanConverter : IValueConverter + internal class EnumToBooleanConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { if (value == null || parameter == null) return false; - return value.ToString().Equals(parameter.ToString(), StringComparison.InvariantCultureIgnoreCase); + var leftValue = value?.ToString(); + var rightValue = value?.ToString(); + + return string.Equals(leftValue, rightValue, StringComparison.InvariantCultureIgnoreCase); } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) diff --git a/Workbench/Converters/InvertableBooleanToVisibilityConverter.cs b/Workbench/Converters/InvertableBooleanToVisibilityConverter.cs index b1f5aa2..0f9b41e 100644 --- a/Workbench/Converters/InvertableBooleanToVisibilityConverter.cs +++ b/Workbench/Converters/InvertableBooleanToVisibilityConverter.cs @@ -13,7 +13,7 @@ namespace Serein.Workbench.Converters /// 根据bool类型控制可见性 /// [ValueConversion(typeof(bool), typeof(Visibility))] - public class InvertableBooleanToVisibilityConverter : IValueConverter + internal class InvertableBooleanToVisibilityConverter : IValueConverter { enum Parameters { diff --git a/Workbench/Converters/MethodDetailsSelectorConverter.cs b/Workbench/Converters/MethodDetailsSelectorConverter.cs index dbd7331..a5363f6 100644 --- a/Workbench/Converters/MethodDetailsSelectorConverter.cs +++ b/Workbench/Converters/MethodDetailsSelectorConverter.cs @@ -8,7 +8,7 @@ using System.Windows.Data; namespace Serein.Workbench.Converters { - public class MethodDetailsSelectorConverter : IMultiValueConverter + internal class MethodDetailsSelectorConverter : IMultiValueConverter { public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { diff --git a/Workbench/Converters/ThumbPositionConverter.cs b/Workbench/Converters/ThumbPositionConverter.cs index 90bf6df..fcaa1bb 100644 --- a/Workbench/Converters/ThumbPositionConverter.cs +++ b/Workbench/Converters/ThumbPositionConverter.cs @@ -5,7 +5,7 @@ namespace Serein.Workbench.Converters /// /// 画布拉动范围距离计算器 /// - public class RightThumbPositionConverter : IValueConverter + internal class RightThumbPositionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { @@ -22,7 +22,7 @@ namespace Serein.Workbench.Converters /// /// 画布拉动范围距离计算器 /// - public class BottomThumbPositionConverter : IValueConverter + internal class BottomThumbPositionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { @@ -39,7 +39,7 @@ namespace Serein.Workbench.Converters /// /// 画布拉动范围距离计算器 /// - public class VerticalCenterThumbPositionConverter : IValueConverter + internal class VerticalCenterThumbPositionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { @@ -56,7 +56,7 @@ namespace Serein.Workbench.Converters /// /// 画布拉动范围距离计算器 /// - public class HorizontalCenterThumbPositionConverter : IValueConverter + internal class HorizontalCenterThumbPositionConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) { diff --git a/Workbench/Converters/TypeToColorConverter.cs b/Workbench/Converters/TypeToColorConverter.cs index 198d3bd..a68f1f7 100644 --- a/Workbench/Converters/TypeToColorConverter.cs +++ b/Workbench/Converters/TypeToColorConverter.cs @@ -8,7 +8,7 @@ namespace Serein.Workbench.Converters /// /// 根据控件类型切换颜色 /// - public class TypeToColorConverter : IValueConverter + internal class TypeToColorConverter : IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { diff --git a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs index 0630b65..38eec08 100644 --- a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs +++ b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs @@ -42,8 +42,12 @@ namespace Serein.Workbench.Customs /// public partial class FlowMethodInfoListBox : UserControl, System.ComponentModel.INotifyPropertyChanged { - private object viewMethodInfo; - public object ViewMethodInfo + private object? viewMethodInfo; + + /// + /// 当前选中的方法信息 + /// + public object? ViewMethodInfo { get => viewMethodInfo; set @@ -56,27 +60,44 @@ namespace Serein.Workbench.Customs } } - public event System.ComponentModel.PropertyChangedEventHandler PropertyChanged; + /// + /// 属性改变事件,用于通知绑定的UI更新 + /// + public event System.ComponentModel.PropertyChangedEventHandler? PropertyChanged; + /// + /// FlowMethodInfoListBox 的构造函数 + /// public FlowMethodInfoListBox() { InitializeComponent(); } - + /// + /// 依赖属性,用于绑定方法信息列表 + /// public static readonly DependencyProperty ItemsSourceProperty = DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(FlowMethodInfoListBox), new PropertyMetadata(null)); + /// + /// 获取或设置方法信息列表 + /// public IEnumerable ItemsSource { get => (IEnumerable)GetValue(ItemsSourceProperty); set => SetValue(ItemsSourceProperty, value); } - public static readonly DependencyProperty BackgroundProperty = + /// + /// 依赖属性,用于设置背景颜色 + /// + public new static readonly DependencyProperty BackgroundProperty = DependencyProperty.Register(nameof(Background), typeof(Brush), typeof(FlowMethodInfoListBox), new PropertyMetadata(Brushes.Transparent)); - public Brush Background + /// + /// 获取或设置背景颜色 + /// + public new Brush Background { get => (Brush)GetValue(BackgroundProperty); set => SetValue(BackgroundProperty, value); diff --git a/Workbench/Extension/MyExtension.cs b/Workbench/Extension/MyExtension.cs index 0bacb12..5abc9ae 100644 --- a/Workbench/Extension/MyExtension.cs +++ b/Workbench/Extension/MyExtension.cs @@ -7,30 +7,65 @@ using System.Windows; namespace Serein.Workbench.Extension { + /// + /// 点(Point)和向量(Vector)的扩展方法 + /// public static class PointExtension { + /// + /// 将两个点相加,返回一个新的点。 + /// + /// + /// + /// public static Point Add(this Point a, Point b) { return new Point(a.X + b.X, a.Y + b.Y); } + /// + /// 将两个点相减,返回一个新的点。 + /// + /// + /// + /// public static Point Sub(this Point a, Point b) { return new Point(a.X - b.X, a.Y - b.Y); } + /// + /// 将点转换为向量。 + /// + /// + /// public static Vector ToVector(this Point me) { return new Vector(me.X, me.Y); } } + + /// + /// 向量(Vector)的扩展方法 + /// public static class VectorExtension { + /// + /// 计算两个向量的点积。 + /// + /// + /// + /// public static double DotProduct(this Vector a, Vector b) { return a.X * b.X + a.Y * b.Y; } + /// + /// 计算两个向量的叉积。 + /// + /// + /// public static Vector NormalizeTo(this Vector v) { var temp = v; diff --git a/Workbench/LogWindow.xaml.cs b/Workbench/LogWindow.xaml.cs index b0e20ec..50674dc 100644 --- a/Workbench/LogWindow.xaml.cs +++ b/Workbench/LogWindow.xaml.cs @@ -1,16 +1,13 @@ -using System.Windows; +using System; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using System.Timers; +using System.Windows; +using Timer = System.Timers.Timer; namespace Serein.Workbench { - /// - /// DebugWindow.xaml 的交互逻辑 - /// - using System; - using System.IO; - using System.Text; - using System.Threading.Tasks; - using System.Timers; - using System.Windows; /// /// LogWindow.xaml 的交互逻辑 @@ -18,6 +15,10 @@ namespace Serein.Workbench public partial class LogWindow : Window { private static LogWindow instance = new LogWindow(); + + /// + /// 获取日志窗口的单例实例 + /// public static LogWindow Instance => instance; @@ -29,6 +30,9 @@ namespace Serein.Workbench private int flushThreshold = 5; // 设置日志刷新阈值 private const int maxFlushSize = 1000; // 每次最大刷新字符数 + /// + /// 日志窗口构造函数,初始化组件和定时器 + /// public LogWindow() { InitializeComponent(); diff --git a/Workbench/Models/TabModel.cs b/Workbench/Models/TabModel.cs index 6197c24..00bf088 100644 --- a/Workbench/Models/TabModel.cs +++ b/Workbench/Models/TabModel.cs @@ -15,30 +15,13 @@ using System.Xml.Linq; namespace Serein.Workbench.Models { + /// + /// FlowEditorTabModel 类表示一个流程编辑器的标签模型。 + /// public partial class FlowEditorTabModel : ObservableObject { - /// - /// tab 名称 - /// - /* public string Name - { - get - { - - var vm = (FlowCanvasViewModel)Content.DataContext; - return vm.Model.Name ?? "null"; - } - set - { - var vm = (FlowCanvasViewModel)Content.DataContext; - vm.Model.Name = value; - OnPropertyChanged(nameof(Name)); - } - } -*/ - [ObservableProperty] - private FlowCanvasDetails _model; + private FlowCanvasDetails? _model; /// @@ -57,8 +40,12 @@ namespace Serein.Workbench.Models /// tab对应的控件 /// [ObservableProperty] - private FlowCanvasView content; + private FlowCanvasView? content; + /// + /// FlowEditorTabModel 构造函数 + /// + /// public FlowEditorTabModel(FlowCanvasView content) { diff --git a/Workbench/Node/INodeJunction.cs b/Workbench/Node/INodeJunction.cs index ae74d0d..2a571ba 100644 --- a/Workbench/Node/INodeJunction.cs +++ b/Workbench/Node/INodeJunction.cs @@ -14,7 +14,7 @@ namespace Serein.Workbench.Node /// /// 约束一个节点应该有哪些控制点 /// - public interface INodeJunction + internal interface INodeJunction { /// /// 方法执行入口控制点 diff --git a/Workbench/Node/Junction/ConnectionLineShape.cs b/Workbench/Node/Junction/ConnectionLineShape.cs index c573dbe..e655123 100644 --- a/Workbench/Node/Junction/ConnectionLineShape.cs +++ b/Workbench/Node/Junction/ConnectionLineShape.cs @@ -35,7 +35,7 @@ namespace Serein.Workbench.Node.View /// public class ConnectionLineShape : Shape { - private readonly double strokeThickness; + //private readonly double strokeThickness; private readonly LineType lineType; @@ -47,6 +47,7 @@ namespace Serein.Workbench.Node.View /// 结束坐标 /// 颜色 /// 是否为虚线 + /// 元素是否在画布上置顶 public ConnectionLineShape(LineType lineType, Point start, Point end, @@ -58,7 +59,7 @@ namespace Serein.Workbench.Node.View this.brush = brush; startPoint = start; endPoint = end; - this.strokeThickness = 4; + //this.strokeThickness = 4; InitElementPoint(isDotted, isTop); _ = Task.Run(async () => @@ -72,6 +73,11 @@ namespace Serein.Workbench.Node.View } + /// + /// 初始化连接线元素点 + /// + /// + /// public void InitElementPoint(bool isDotted , bool isTop = false) { hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线 @@ -148,8 +154,8 @@ namespace Serein.Workbench.Node.View #region 重绘 private readonly StreamGeometry streamGeometry = new StreamGeometry(); - private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心 - private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心 + //private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心 + //private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心 private Pen hitVisiblePen; // 初始化碰撞检测线 private Pen visualPen; // 默认可视化Pen private Point startPoint; // 连接线的起始节点 @@ -158,8 +164,16 @@ namespace Serein.Workbench.Node.View private double opacity; // 透明度 double linkSize; // 根据缩放比例调整线条粗细 + + /// + /// 定义几何形状 + /// protected override Geometry DefiningGeometry => streamGeometry; - + + /// + /// 更新线条颜色 + /// + /// public void UpdateLineColor(Brush brush) { visualPen = new Pen(brush, 3.0); // 默认可视化Pen diff --git a/Workbench/Node/Junction/JunctionControlBase.cs b/Workbench/Node/Junction/JunctionControlBase.cs index 029c1f8..33c2748 100644 --- a/Workbench/Node/Junction/JunctionControlBase.cs +++ b/Workbench/Node/Junction/JunctionControlBase.cs @@ -46,7 +46,7 @@ namespace Serein.Workbench.Node.View /// /// 入参控件 /// - public class ParamsArgControl: Shape + internal class ParamsArgControl: Shape { public ParamsArgControl() { @@ -172,7 +172,7 @@ namespace Serein.Workbench.Node.View - public abstract class JunctionControlBase : Shape + internal abstract class JunctionControlBase : Shape { private readonly FlowNodeService flowNodeService; protected JunctionControlBase() @@ -262,16 +262,12 @@ namespace Serein.Workbench.Node.View - /// - /// 禁止连接 - /// - private bool IsConnectionDisable; /// /// 处理鼠标悬停状态 /// private bool _isMouseOver; - public bool IsMouseOver + public new bool IsMouseOver { get => _isMouseOver; set diff --git a/Workbench/Node/Junction/JunctionData.cs b/Workbench/Node/Junction/JunctionData.cs index aa270b0..e63580b 100644 --- a/Workbench/Node/Junction/JunctionData.cs +++ b/Workbench/Node/Junction/JunctionData.cs @@ -14,7 +14,7 @@ namespace Serein.Workbench.Node.View { #region Model,不科学的全局变量 - public class MyLine + internal class MyLine { public MyLine(Canvas canvas, ConnectionLineShape line) { @@ -32,7 +32,7 @@ namespace Serein.Workbench.Node.View } } - public class ConnectingData + internal class ConnectingData { /// @@ -42,11 +42,11 @@ namespace Serein.Workbench.Node.View /// /// 起始控制点 /// - public JunctionControlBase StartJunction { get; set; } + public JunctionControlBase? StartJunction { get; set; } /// /// 当前的控制点 /// - public JunctionControlBase CurrentJunction { get; set; } + public JunctionControlBase? CurrentJunction { get; set; } /// /// 开始坐标 /// @@ -54,7 +54,7 @@ namespace Serein.Workbench.Node.View /// /// 线条样式 /// - public MyLine MyLine { get; set; } + public MyLine? MyLine { get; set; } /// /// 线条类别(方法调用) @@ -68,7 +68,7 @@ namespace Serein.Workbench.Node.View /// /// 判断当前连接类型 /// - public JunctionOfConnectionType Type => StartJunction.JunctionType.ToConnectyionType(); + public JunctionOfConnectionType Type => StartJunction?.JunctionType.ToConnectyionType() ?? JunctionOfConnectionType.None; /// @@ -113,11 +113,11 @@ namespace Serein.Workbench.Node.View if (StartJunction.JunctionType == Library.JunctionType.Execute || StartJunction.JunctionType == Library.JunctionType.ArgData) { - MyLine.Line.UpdateStartPoints(point); + MyLine?.Line.UpdateStartPoints(point); } else { - MyLine.Line.UpdateEndPoints(point); + MyLine?.Line.UpdateEndPoints(point); } } diff --git a/Workbench/Node/Junction/View/ArgJunctionControl.cs b/Workbench/Node/Junction/View/ArgJunctionControl.cs index a16dc07..3c33d92 100644 --- a/Workbench/Node/Junction/View/ArgJunctionControl.cs +++ b/Workbench/Node/Junction/View/ArgJunctionControl.cs @@ -5,7 +5,7 @@ using Serein.Library; namespace Serein.Workbench.Node.View { - public class ArgJunctionControl : JunctionControlBase + internal class ArgJunctionControl : JunctionControlBase { public ArgJunctionControl() { diff --git a/Workbench/Node/Junction/View/ExecuteJunctionControl.cs b/Workbench/Node/Junction/View/ExecuteJunctionControl.cs index e32f5f3..be61ae7 100644 --- a/Workbench/Node/Junction/View/ExecuteJunctionControl.cs +++ b/Workbench/Node/Junction/View/ExecuteJunctionControl.cs @@ -6,7 +6,7 @@ using Serein.Library; namespace Serein.Workbench.Node.View { - public class ExecuteJunctionControl : JunctionControlBase + internal class ExecuteJunctionControl : JunctionControlBase { public ExecuteJunctionControl() { diff --git a/Workbench/Node/Junction/View/NextStepJunctionControl.cs b/Workbench/Node/Junction/View/NextStepJunctionControl.cs index 7c5bde9..372f085 100644 --- a/Workbench/Node/Junction/View/NextStepJunctionControl.cs +++ b/Workbench/Node/Junction/View/NextStepJunctionControl.cs @@ -6,7 +6,7 @@ using Serein.Library; namespace Serein.Workbench.Node.View { - public class NextStepJunctionControl : JunctionControlBase + internal class NextStepJunctionControl : JunctionControlBase { //public override JunctionType JunctionType { get; } = JunctionType.NextStep; public NextStepJunctionControl() diff --git a/Workbench/Node/Junction/View/ResultJunctionControl.cs b/Workbench/Node/Junction/View/ResultJunctionControl.cs index c0d8c88..61f8c9c 100644 --- a/Workbench/Node/Junction/View/ResultJunctionControl.cs +++ b/Workbench/Node/Junction/View/ResultJunctionControl.cs @@ -6,7 +6,7 @@ using Serein.Library; namespace Serein.Workbench.Node.View { - public class ResultJunctionControl : JunctionControlBase + internal class ResultJunctionControl : JunctionControlBase { public ResultJunctionControl() { diff --git a/Workbench/Node/NodeControlBase.cs b/Workbench/Node/NodeControlBase.cs index 01919a8..7dd3f75 100644 --- a/Workbench/Node/NodeControlBase.cs +++ b/Workbench/Node/NodeControlBase.cs @@ -13,7 +13,7 @@ namespace Serein.Workbench.Node.View /// /// 节点控件基类(控件) /// - public abstract class NodeControlBase : UserControl //, IDynamicFlowNode + public abstract class NodeControlBase : UserControl { /// /// 节点所在的画布(以后需要将画布封装出来,实现多画布的功能) @@ -34,12 +34,18 @@ namespace Serein.Workbench.Node.View public NodeControlViewModelBase ViewModel { get; set; } - + /// + /// 节点控件基类,所有节点控件都需要继承这个类 + /// protected NodeControlBase() { this.Background = Brushes.Transparent; } + /// + /// 节点控件基类,所有节点控件都需要继承这个类 + /// + /// protected NodeControlBase(NodeControlViewModelBase viewModelBase) { ViewModel = viewModelBase; @@ -85,7 +91,7 @@ namespace Serein.Workbench.Node.View /// 添加与该节点有关的连接后,记录下来 /// /// - public void AddCnnection(ConnectionControl connection) + internal void AddCnnection(ConnectionControl connection) { connectionControls.Add(connection); } @@ -94,7 +100,7 @@ namespace Serein.Workbench.Node.View /// 删除了连接之后,还需要从节点中的记录移除 /// /// - public void RemoveConnection(ConnectionControl connection) + internal void RemoveConnection(ConnectionControl connection) { connectionControls.Remove(connection); connection.Remove(); // 主动删除连接 @@ -163,7 +169,7 @@ namespace Serein.Workbench.Node.View return null; } - protected static JunctionControlBase[] GetArgJunction(NodeControlBase nodeControl, MethodDetailsControl methodDetailsControl) + internal static JunctionControlBase[] GetArgJunction(NodeControlBase nodeControl, MethodDetailsControl methodDetailsControl) { // 获取 MethodDetailsControl 实例 try diff --git a/Workbench/Node/NodeControlViewModelBase.cs b/Workbench/Node/NodeControlViewModelBase.cs index a876b06..77f35db 100644 --- a/Workbench/Node/NodeControlViewModelBase.cs +++ b/Workbench/Node/NodeControlViewModelBase.cs @@ -17,14 +17,16 @@ namespace Serein.Workbench.Node.ViewModel ///// public IFlowNode NodeModel { get; } + /// + /// 节点控制器的基类 + /// + /// public NodeControlViewModelBase(IFlowNode nodeModel) { NodeModel = nodeModel; } - - /// /// 工作台预览基本节点时,避免其中的文本框响应拖拽事件导致卡死 /// @@ -32,13 +34,5 @@ namespace Serein.Workbench.Node.ViewModel private bool isEnabledOnView = true; - - public event PropertyChangedEventHandler? PropertyChanged; - protected void OnPropertyChanged([CallerMemberName] string propertyName = null) - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - - } } diff --git a/Workbench/Node/View/ActionNodeControl.xaml.cs b/Workbench/Node/View/ActionNodeControl.xaml.cs index bcd728a..8a155c8 100644 --- a/Workbench/Node/View/ActionNodeControl.xaml.cs +++ b/Workbench/Node/View/ActionNodeControl.xaml.cs @@ -14,6 +14,10 @@ namespace Serein.Workbench.Node.View /// public partial class ActionNodeControl : NodeControlBase, INodeJunction { + /// + /// 构造函数,传入ViewModel + /// + /// public ActionNodeControl(ActionNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; diff --git a/Workbench/Node/View/ConditionNodeControl.xaml.cs b/Workbench/Node/View/ConditionNodeControl.xaml.cs index 0e9d46b..c456b5b 100644 --- a/Workbench/Node/View/ConditionNodeControl.xaml.cs +++ b/Workbench/Node/View/ConditionNodeControl.xaml.cs @@ -10,6 +10,9 @@ namespace Serein.Workbench.Node.View /// public partial class ConditionNodeControl : NodeControlBase, INodeJunction { + /// + /// 条件节点控件(用于条件控件) + /// public ConditionNodeControl() : base() { @@ -22,6 +25,11 @@ namespace Serein.Workbench.Node.View InitializeComponent(); } + /// + /// 条件节点控件(用于条件控件) + /// + /// + public ConditionNodeControl(ConditionNodeControlViewModel viewModel):base(viewModel) { DataContext = viewModel; diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs index 2fa7136..491ccf2 100644 --- a/Workbench/Node/View/ConnectionControl.cs +++ b/Workbench/Node/View/ConnectionControl.cs @@ -18,7 +18,9 @@ namespace Serein.Workbench.Node.View { #region 连接点相关代码 - + /// + /// 连接点类型 + /// public class ConnectionModelBase { @@ -48,8 +50,14 @@ namespace Serein.Workbench.Node.View } + /// + /// 连接点类型 + /// public interface IJunctionNode { + /// + /// 连接点所属Guid + /// string BoundNodeGuid { get; } } @@ -73,26 +81,26 @@ namespace Serein.Workbench.Node.View } - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + @@ -104,7 +112,7 @@ namespace Serein.Workbench.Node.View /// /// 连接控件,表示控件的连接关系 /// - public class ConnectionControl + internal class ConnectionControl { /// /// 所在的画布 @@ -155,7 +163,9 @@ namespace Serein.Workbench.Node.View /// /// /// - public ConnectionControl(Canvas Canvas, + /// + /// + internal ConnectionControl(Canvas Canvas, ConnectionInvokeType invokeType, JunctionControlBase Start, JunctionControlBase End) @@ -169,10 +179,15 @@ namespace Serein.Workbench.Node.View } /// - /// 关于入参 + /// 关于入参 /// + /// /// - /// + /// + /// + /// + /// + /// public ConnectionControl(LineType LineType, Canvas Canvas, int argIndex, diff --git a/Workbench/Node/View/ExpOpNodeControl.xaml.cs b/Workbench/Node/View/ExpOpNodeControl.xaml.cs index 8112768..2378d68 100644 --- a/Workbench/Node/View/ExpOpNodeControl.xaml.cs +++ b/Workbench/Node/View/ExpOpNodeControl.xaml.cs @@ -10,6 +10,9 @@ namespace Serein.Workbench.Node.View /// public partial class ExpOpNodeControl : NodeControlBase, INodeJunction { + /// + /// 表达式操作节点控件 + /// public ExpOpNodeControl() : base() { // 窗体初始化需要 @@ -20,6 +23,11 @@ namespace Serein.Workbench.Node.View DataContext = ViewModel; InitializeComponent(); } + + /// + /// 表达式操作节点控件构造函数,使用指定的 ViewModel 初始化 + /// + /// public ExpOpNodeControl(ExpOpNodeControlViewModel viewModel) :base(viewModel) { DataContext = viewModel; diff --git a/Workbench/Node/View/FlipflopNodeControl.xaml.cs b/Workbench/Node/View/FlipflopNodeControl.xaml.cs index f9c1e73..47e5341 100644 --- a/Workbench/Node/View/FlipflopNodeControl.xaml.cs +++ b/Workbench/Node/View/FlipflopNodeControl.xaml.cs @@ -9,6 +9,10 @@ namespace Serein.Workbench.Node.View /// public partial class FlipflopNodeControl : NodeControlBase, INodeJunction { + /// + /// 构造函数 + /// + /// public FlipflopNodeControl(FlipflopNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; diff --git a/Workbench/Node/View/FlowCallNodeControl.xaml.cs b/Workbench/Node/View/FlowCallNodeControl.xaml.cs index 4512c31..13a2322 100644 --- a/Workbench/Node/View/FlowCallNodeControl.xaml.cs +++ b/Workbench/Node/View/FlowCallNodeControl.xaml.cs @@ -14,6 +14,10 @@ namespace Serein.Workbench.Node.View public partial class FlowCallNodeControl : NodeControlBase, INodeJunction { private new FlowCallNodeControlViewModel ViewModel { get; set; } + + /// + /// 流程接口节点控件构造函数 + /// public FlowCallNodeControl() { var env = App.GetService(); @@ -23,6 +27,11 @@ namespace Serein.Workbench.Node.View base.ViewModel.NodeModel.DisplayName = "[流程接口]"; InitializeComponent(); } + + /// + /// 流程接口节点控件构造函数,传入ViewModel + /// + /// public FlowCallNodeControl(FlowCallNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; diff --git a/Workbench/Node/View/GlobalDataControl.xaml.cs b/Workbench/Node/View/GlobalDataControl.xaml.cs index ed6a5f7..93de7b7 100644 --- a/Workbench/Node/View/GlobalDataControl.xaml.cs +++ b/Workbench/Node/View/GlobalDataControl.xaml.cs @@ -13,10 +13,14 @@ namespace Serein.Workbench.Node.View { private readonly GlobalDataNodeControlViewModel viewModel; + /// + /// 全局数据控件构造函数,使用默认的全局数据节点模型 + /// public GlobalDataControl() : base() { // 窗体初始化需要 var env = App.GetService(); + viewModel = new GlobalDataNodeControlViewModel(new SingleGlobalDataNode(env)); base.ViewModel = new GlobalDataNodeControlViewModel(new SingleGlobalDataNode(env)); base.ViewModel.IsEnabledOnView = false; base.ViewModel.NodeModel.DisplayName = "[全局数据]"; @@ -24,6 +28,11 @@ namespace Serein.Workbench.Node.View InitializeComponent(); } + + /// + /// 全局数据控件构造函数,使用指定的全局数据节点模型 + /// + /// public GlobalDataControl(GlobalDataNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; @@ -54,6 +63,11 @@ namespace Serein.Workbench.Node.View JunctionControlBase[] INodeJunction.ArgDataJunction => throw new NotImplementedException(); + /// + /// 放置节点控件到全局数据面板中 + /// + /// + /// public bool PlaceNode(NodeControlBase nodeControl) { if (GlobalDataPanel.Children.Contains(nodeControl)) @@ -65,6 +79,11 @@ namespace Serein.Workbench.Node.View return true; } + /// + /// 从全局数据面板中取出节点控件 + /// + /// + /// public bool TakeOutNode(NodeControlBase nodeControl) { if (!GlobalDataPanel.Children.Contains(nodeControl)) @@ -75,6 +94,9 @@ namespace Serein.Workbench.Node.View return true; } + /// + /// 取出所有节点控件(用于删除容器) + /// public void TakeOutAll() { GlobalDataPanel.Children.Clear(); diff --git a/Workbench/Node/View/NetScriptNodeControl.xaml.cs b/Workbench/Node/View/NetScriptNodeControl.xaml.cs index cef6912..58e1898 100644 --- a/Workbench/Node/View/NetScriptNodeControl.xaml.cs +++ b/Workbench/Node/View/NetScriptNodeControl.xaml.cs @@ -58,7 +58,6 @@ namespace Serein.Workbench.Node.View JunctionControlBase INodeJunction.ReturnDataJunction => throw new Exception(); - public JunctionControlBase[] ArgDataJunction => []; - + JunctionControlBase[] INodeJunction.ArgDataJunction => []; } } diff --git a/Workbench/Node/View/ScriptNodeControl.xaml.cs b/Workbench/Node/View/ScriptNodeControl.xaml.cs index e542502..3208d61 100644 --- a/Workbench/Node/View/ScriptNodeControl.xaml.cs +++ b/Workbench/Node/View/ScriptNodeControl.xaml.cs @@ -26,9 +26,11 @@ namespace Serein.Workbench.Node.View public partial class ScriptNodeControl : NodeControlBase , INodeJunction { private ScriptNodeControlViewModel viewModel => (ScriptNodeControlViewModel)ViewModel; - private DispatcherTimer _debounceTimer; // 用于延迟更新 private bool _isUpdating = false; // 防止重复更新 + /// + /// BaseNodesView.xaml 准备节点预览入口 + /// public ScriptNodeControl() { @@ -39,6 +41,11 @@ namespace Serein.Workbench.Node.View viewModel.NodeModel.DisplayName = "[脚本节点]"; InitializeComponent(); } + + /// + /// 流程运行环境创建节点入口 + /// + /// public ScriptNodeControl(ScriptNodeControlViewModel viewModel) : base(viewModel) { DataContext = viewModel; diff --git a/Workbench/Node/View/UINodeControl.xaml.cs b/Workbench/Node/View/UINodeControl.xaml.cs index 0701389..b9784b0 100644 --- a/Workbench/Node/View/UINodeControl.xaml.cs +++ b/Workbench/Node/View/UINodeControl.xaml.cs @@ -30,25 +30,23 @@ namespace Serein.Workbench.Node.View InitializeComponent(); } - public UINodeControl(UINodeControlViewModel viewModel) : base(viewModel) + internal UINodeControl(UINodeControlViewModel viewModel) : base(viewModel) { ViewModel = viewModel; DataContext = viewModel; ViewModel.NodeModel.DisplayName = "[流程UI]"; InitializeComponent(); - - } - public JunctionControlBase ExecuteJunction => this.ExecuteJunctionControl; + JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl; - public JunctionControlBase NextStepJunction => throw new NotImplementedException(); + JunctionControlBase INodeJunction.NextStepJunction => throw new NotImplementedException(); - public JunctionControlBase[] ArgDataJunction => throw new NotImplementedException(); + JunctionControlBase[] INodeJunction.ArgDataJunction => throw new NotImplementedException(); - public JunctionControlBase ReturnDataJunction => throw new NotImplementedException(); + JunctionControlBase INodeJunction.ReturnDataJunction => throw new NotImplementedException(); private void NodeControlBase_Loaded(object sender, RoutedEventArgs e) diff --git a/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs b/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs index 2dbec20..2b5c87a 100644 --- a/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/ActionNodeControlViewModel.cs @@ -4,8 +4,15 @@ using Serein.Workbench.Node.View; namespace Serein.Workbench.Node.ViewModel { + /// + /// ActionNodeControlViewModel 类用于表示单动作节点的控制视图模型。 + /// public class ActionNodeControlViewModel : NodeControlViewModelBase { + /// + /// 构造一个新的 ActionNodeControlViewModel 实例。 + /// + /// public ActionNodeControlViewModel(SingleActionNode node) : base(node) { // this.NodelModel = node; diff --git a/Workbench/Node/ViewModel/ConditionNodeControlViewModel.cs b/Workbench/Node/ViewModel/ConditionNodeControlViewModel.cs index 0a545a0..7e386ea 100644 --- a/Workbench/Node/ViewModel/ConditionNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/ConditionNodeControlViewModel.cs @@ -10,7 +10,7 @@ namespace Serein.Workbench.Node.ViewModel public class ConditionNodeControlViewModel : NodeControlViewModelBase { - public new SingleConditionNode NodeModel { get; } + private new SingleConditionNode NodeModel { get; } /// /// 是否为自定义参数 diff --git a/Workbench/Node/ViewModel/ExpOpNodeControlViewModel.cs b/Workbench/Node/ViewModel/ExpOpNodeControlViewModel.cs index db4af16..87e04d1 100644 --- a/Workbench/Node/ViewModel/ExpOpNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/ExpOpNodeControlViewModel.cs @@ -4,9 +4,15 @@ using Serein.Workbench.Node.View; namespace Serein.Workbench.Node.ViewModel { + /// + /// 表达式操作节点控制视图模型 + /// public class ExpOpNodeControlViewModel: NodeControlViewModelBase { - public new SingleExpOpNode NodeModel { get; } + /// + /// 对应的表达式操作节点模型 + /// + public new SingleExpOpNode NodeModel { get; } //public string Expression //{ @@ -18,6 +24,10 @@ namespace Serein.Workbench.Node.ViewModel // } //} + /// + /// 表达式操作节点控制视图模型构造函数 + /// + /// public ExpOpNodeControlViewModel(SingleExpOpNode nodeModel) : base(nodeModel) { diff --git a/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs b/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs index d7829e3..73d53e9 100644 --- a/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/FlipflopNodeControlViewModel.cs @@ -4,10 +4,21 @@ using Serein.Workbench.Node.View; namespace Serein.Workbench.Node.ViewModel { + /// + /// 单触发器节点控制视图模型 + /// public class FlipflopNodeControlViewModel : NodeControlViewModelBase { - public new SingleFlipflopNode NodelModel { get;} - public FlipflopNodeControlViewModel(SingleFlipflopNode node) : base(node) + /// + /// 单触发器节点模型 + /// + public SingleFlipflopNode NodelModel { get;} + + /// + /// 构造一个新的单触发器节点控制视图模型实例。 + /// + /// + public FlipflopNodeControlViewModel(SingleFlipflopNode node) : base(node) { this.NodelModel = node; } diff --git a/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs b/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs index ef5ed3c..ef1a794 100644 --- a/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs @@ -50,6 +50,10 @@ namespace Serein.Workbench.Node.ViewModel private readonly FlowNodeService flowNodeService; private readonly IFlowEEForwardingService flowEEForwardingService; + /// + /// 流程接口节点构造函数 + /// + /// public FlowCallNodeControlViewModel(SingleFlowCallNode node) : base(node) { this.FlowCallNode = node; diff --git a/Workbench/Node/ViewModel/GlobalDataNodeControlViewModel.cs b/Workbench/Node/ViewModel/GlobalDataNodeControlViewModel.cs index 8bc19da..8fb7fda 100644 --- a/Workbench/Node/ViewModel/GlobalDataNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/GlobalDataNodeControlViewModel.cs @@ -5,9 +5,12 @@ using System.Windows.Input; namespace Serein.Workbench.Node.ViewModel { + /// + /// 全局数据节点控制视图模型 + /// public class GlobalDataNodeControlViewModel : NodeControlViewModelBase { - private SingleGlobalDataNode NodeModel => (SingleGlobalDataNode)base.NodeModel; + private new SingleGlobalDataNode NodeModel => (SingleGlobalDataNode)base.NodeModel; /// /// 复制全局数据表达式 @@ -19,7 +22,10 @@ namespace Serein.Workbench.Node.ViewModel /// public ICommand CommandRefreshData { get; } - + /// + /// 全局数据节点控制视图模型构造函数 + /// + /// public GlobalDataNodeControlViewModel(SingleGlobalDataNode node) : base(node) { CommandCopyDataExp = new RelayCommand( o => diff --git a/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs b/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs index f18b1f9..16d9e75 100644 --- a/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/NetScriptNodeControlViewModel.cs @@ -6,9 +6,12 @@ using System.Windows.Input; namespace Serein.Workbench.Node.ViewModel { + /// + /// 动态脚本节点控制视图模型 + /// public class NetScriptNodeControlViewModel : NodeControlViewModelBase { - private SingleNetScriptNode NodeModel => (SingleNetScriptNode)base.NodeModel; + private new SingleNetScriptNode NodeModel => (SingleNetScriptNode)base.NodeModel; public string Tips { @@ -45,22 +48,12 @@ public class FlowLibrary } }"; - CommandOpenScriptEdit = new RelayCommand(async o => + CommandOpenScriptEdit = new RelayCommand(o => { DynamicCompilerView dynamicCompilerView = new DynamicCompilerView(); dynamicCompilerView.ScriptCode = this.Script ; dynamicCompilerView.OnCompileComplete = OnCompileComplete; dynamicCompilerView.ShowDialog(); - - //try - //{ - // var result = await NodeModel.ExecutingAsync(new Library.DynamicContext(nodeModel.Env)); - // nodeModel.Env.WriteLine(InfoType.INFO, result?.ToString()); - //} - //catch (Exception ex) - //{ - // nodeModel.Env.WriteLine(InfoType.ERROR, ex.ToString()); - //} }); NodeModel1 = nodeModel; } @@ -86,6 +79,10 @@ public class FlowLibrary /// 打开编辑窗口 /// public ICommand CommandOpenScriptEdit { get; } + + /// + /// 节点模型 + /// public NodeModelBase NodeModel1 { get; } } } diff --git a/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs b/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs index 31ab71a..a74c516 100644 --- a/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/ScriptNodeControlViewModel.cs @@ -13,9 +13,13 @@ using System.Windows.Input; namespace Serein.Workbench.Node.ViewModel { + /// + /// 脚本节点控制视图模型 + /// public class ScriptNodeControlViewModel : NodeControlViewModelBase { - private SingleScriptNode NodeModel => (SingleScriptNode)base.NodeModel; + + private new SingleScriptNode NodeModel => (SingleScriptNode)base.NodeModel; public string? Script { diff --git a/Workbench/Node/ViewModel/UINodeControlViewModel.cs b/Workbench/Node/ViewModel/UINodeControlViewModel.cs index 7d70ce3..a053a68 100644 --- a/Workbench/Node/ViewModel/UINodeControlViewModel.cs +++ b/Workbench/Node/ViewModel/UINodeControlViewModel.cs @@ -12,9 +12,13 @@ using System.Windows.Controls; namespace Serein.Workbench.Node.ViewModel { + /// + /// UI节点控制器视图模型 + /// + public partial class UINodeControlViewModel : NodeControlViewModelBase { - private SingleUINode NodeModel => (SingleUINode)base.NodeModel; + private new SingleUINode NodeModel => (SingleUINode)base.NodeModel; //public IEmbeddedContent Adapter => NodeModel.Adapter; /// diff --git a/Workbench/Serein.Workbench.csproj b/Workbench/Serein.Workbench.csproj index a597d11..2308c5d 100644 --- a/Workbench/Serein.Workbench.csproj +++ b/Workbench/Serein.Workbench.csproj @@ -11,7 +11,11 @@ true - + + + 1701;1702;1573;CS0414 + + diff --git a/Workbench/Services/FlowNodeService.cs b/Workbench/Services/FlowNodeService.cs index df6b1f7..33749c4 100644 --- a/Workbench/Services/FlowNodeService.cs +++ b/Workbench/Services/FlowNodeService.cs @@ -18,7 +18,7 @@ namespace Serein.Workbench.Services /// /// 流程节点管理 /// - public class FlowNodeService + internal class FlowNodeService { @@ -120,7 +120,7 @@ namespace Serein.Workbench.Services /// /// 连接数据 /// - public ConnectingData ConnectingData { get; } = new ConnectingData(); + internal ConnectingData ConnectingData { get; } = new ConnectingData(); #endregion @@ -471,12 +471,12 @@ namespace Serein.Workbench.Services return NodeControls.TryGetValue(nodeGuid, out nodeControl); } - + /// /// 从Guid获取画布视图 /// /// - /// + /// /// private bool TryGetCanvas(string nodeGuid, out FlowCanvasView flowCanvas) { @@ -643,8 +643,7 @@ namespace Serein.Workbench.Services } catch (Exception ex) { - - //SereinEnv.WriteLine(InfoType.ERROR, $"粘贴节点时发生异常:{ex}"); + SereinEnv.WriteLine(InfoType.ERROR, $"粘贴节点时发生异常:{ex}"); } // SereinEnv.WriteLine(InfoType.INFO, $"剪贴板文本内容: {clipboardText}"); } diff --git a/Workbench/Services/FlowProjectService.cs b/Workbench/Services/FlowProjectService.cs index 7af1b17..27f494f 100644 --- a/Workbench/Services/FlowProjectService.cs +++ b/Workbench/Services/FlowProjectService.cs @@ -11,35 +11,46 @@ using System.Threading.Tasks; namespace Serein.Workbench.Services { + /// + /// 流程项目服务 + /// public class FlowProjectService { private readonly IFlowEnvironment flowEnvironment; + /// + /// 流程项目服务 + /// + /// public FlowProjectService(IFlowEnvironment flowEnvironment) { this.flowEnvironment = flowEnvironment; } + /// + /// 启动流程项目管理服务器 + /// public void StartProjectManagementServer() { // CollabrationSideManagement } + /// + /// 加载本地流程项目到当前环境中 + /// + /// public void LoadLocalProject(string filePath) { if (File.Exists(filePath)) { - /* - var dir = Path.GetDirectoryName(filePath); - var flowEnvInfo = new FlowEnvInfo - { - Project = FlowProjectData, - };*/ flowEnvironment.LoadProject(filePath); } } + /// + /// 选择本地流程项目文件并加载到当前环境中 + /// public void SelectProjectFile() { System.Windows.Forms.OpenFileDialog openFileDialog = new System.Windows.Forms.OpenFileDialog(); diff --git a/Workbench/Services/KeyEventService.cs b/Workbench/Services/KeyEventService.cs index 865f6f6..8540b53 100644 --- a/Workbench/Services/KeyEventService.cs +++ b/Workbench/Services/KeyEventService.cs @@ -10,7 +10,16 @@ using static System.Windows.Forms.AxHost; namespace Serein.Workbench.Services { + /// + /// 全局按键事件委托 + /// + /// public delegate void KeyDownEventHandler(Key key); + + /// + /// 全局按键抬起事件委托 + /// + /// public delegate void KeyUpEventHandler(Key key); /// @@ -18,7 +27,14 @@ namespace Serein.Workbench.Services /// public interface IKeyEventService { + /// + /// 按键按下事件 + /// event KeyDownEventHandler OnKeyDown; + + /// + /// 按键抬起事件 + /// event KeyUpEventHandler OnKeyUp; /// @@ -56,6 +72,9 @@ namespace Serein.Workbench.Services /// public event KeyUpEventHandler OnKeyUp; + /// + /// 全局按键事件服务构造函数 + /// public KeyEventService() { var arr = Enum.GetValues(); @@ -66,6 +85,12 @@ namespace Serein.Workbench.Services } private readonly bool[] KeysState; + + /// + /// 获取某个按键的状态 + /// + /// + /// public bool GetKeyState(Key key) { return KeysState[(int)key]; @@ -73,7 +98,10 @@ namespace Serein.Workbench.Services - + /// + /// 按键按下事件 + /// + /// public void KeyDown(Key key) { KeysState[(int)key] = true; @@ -81,6 +109,10 @@ namespace Serein.Workbench.Services //Debug.WriteLine($"按键按下事件:{key}"); } + /// + /// 按键抬起事件 + /// + /// public void KeyUp(Key key) { KeysState[(int)key] = false; diff --git a/Workbench/Themes/BindableRichTextBox.cs b/Workbench/Themes/BindableRichTextBox.cs index f7c9d1e..ccaa928 100644 --- a/Workbench/Themes/BindableRichTextBox.cs +++ b/Workbench/Themes/BindableRichTextBox.cs @@ -9,8 +9,14 @@ using System.Windows; namespace Serein.Workbench.Themes { + /// + /// BindableRichTextBox 是一个可绑定的 RichTextBox 控件,允许将 FlowDocument 对象绑定到其 Document 属性。 + /// public partial class BindableRichTextBox : RichTextBox { + /// + /// BindableRichTextBox 的依赖属性,允许绑定 FlowDocument 对象到 RichTextBox 的 Document 属性。 + /// public new FlowDocument Document { get { return (FlowDocument)GetValue(DocumentProperty); } diff --git a/Workbench/Themes/IOCObjectViewControl.xaml.cs b/Workbench/Themes/IOCObjectViewControl.xaml.cs index 7d231d5..776d17e 100644 --- a/Workbench/Themes/IOCObjectViewControl.xaml.cs +++ b/Workbench/Themes/IOCObjectViewControl.xaml.cs @@ -26,6 +26,9 @@ namespace Serein.Workbench.Themes { public Action SelectObj { get; set; } + /// + /// IOC对象视图控件 + /// public IOCObjectViewControl() { InitializeComponent(); @@ -89,6 +92,9 @@ namespace Serein.Workbench.Themes } } + /// + /// 清空所有实例显示 + /// public void ClearObjItem() { DependenciesListBox.Dispatcher.Invoke(() => @@ -108,6 +114,10 @@ namespace Serein.Workbench.Themes } } + /// + /// 移除一个实例的显示 + /// + /// public void RemoveDependenciesInstance(string key) { object? itemControl = null; diff --git a/Workbench/Themes/InputDialog.xaml.cs b/Workbench/Themes/InputDialog.xaml.cs index 42efeec..7574136 100644 --- a/Workbench/Themes/InputDialog.xaml.cs +++ b/Workbench/Themes/InputDialog.xaml.cs @@ -19,8 +19,14 @@ namespace Serein.Workbench.Themes /// public partial class InputDialog : Window { + /// + /// 输入对话框的输入值 + /// public string InputValue { get; private set; } + /// + /// 初始化输入对话框的新实例 + /// public InputDialog() { InitializeComponent(); diff --git a/Workbench/Themes/MethodDetailsControl.xaml.cs b/Workbench/Themes/MethodDetailsControl.xaml.cs index f4b8b54..18da12d 100644 --- a/Workbench/Themes/MethodDetailsControl.xaml.cs +++ b/Workbench/Themes/MethodDetailsControl.xaml.cs @@ -11,9 +11,19 @@ using System.Windows.Input; namespace Serein.Workbench.Themes { - + /// + /// 描述或名称转换器 + /// public class DescriptionOrNameConverter : IMultiValueConverter { + /// + /// 将源类型的数组转换为目标类型的值。 + /// + /// + /// + /// + /// + /// public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { string description = values[0] as string; @@ -21,15 +31,34 @@ namespace Serein.Workbench.Themes return string.IsNullOrWhiteSpace(description) ? name : description; } + /// + /// 将值从目标类型转换回源类型的数组。 + /// + /// + /// + /// + /// + /// + /// public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } - + /// + /// 多条件转换器,根据参数类型和是否启用来决定返回的模板 + /// public class MultiConditionConverter : IMultiValueConverter { + /// + /// 将源类型的数组转换为目标类型的值。 + /// + /// + /// + /// + /// + /// public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture) { if (values.Length == 2 && values[0] is Type valueType && values[1] is bool isEnabled) @@ -51,30 +80,53 @@ namespace Serein.Workbench.Themes return DependencyProperty.UnsetValue; } + /// + /// 将值从目标类型转换回源类型的数组。 + /// + /// + /// + /// + /// + /// + /// public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture) { throw new NotImplementedException(); } } - + /// + /// 数据上下文代理类,用于绑定到DataContextProperty + /// public class DataContextProxy : Freezable { + /// + /// 数据上下文代理类构造函数 + /// public DataContextProxy() { BindingOperations.SetBinding(this, DataContextProperty, new Binding()); } + /// + /// 数据上下文属性 + /// public ParameterDetails DataContext { get { return (ParameterDetails)GetValue(DataContextProperty); } set { SetValue(DataContextProperty, value); } } - + /// + /// 数据上下文依赖属性 + /// public static readonly DependencyProperty DataContextProperty = FrameworkElement .DataContextProperty.AddOwner(typeof(DataContextProxy)); + /// + /// 创建一个新的实例,重写Freezable的CreateInstanceCore方法。 + /// + /// protected override Freezable CreateInstanceCore() { return new DataContextProxy(); @@ -123,10 +175,15 @@ namespace Serein.Workbench.Themes //var MethodDetails = (MethodDetails)args.NewValue; //MethodDetails.ExplicitDatas[0]. } - + /// + /// 添加参数命令 + /// public ICommand CommandAddParams { get; } + /// + /// 方法参数控件构造函数 + /// public MethodDetailsControl() { CommandAddParams = new RelayCommand(ExecuteAddParams); diff --git a/Workbench/Themes/ObjectViewerControl.xaml.cs b/Workbench/Themes/ObjectViewerControl.xaml.cs index e0ca417..1a6bf73 100644 --- a/Workbench/Themes/ObjectViewerControl.xaml.cs +++ b/Workbench/Themes/ObjectViewerControl.xaml.cs @@ -9,6 +9,9 @@ using static Serein.Workbench.Themes.TypeViewerWindow; namespace Serein.Workbench.Themes { + /// + /// FlowDataDetails 类用于存储对象的详细信息,包括名称、类型、数据类型、数据值和数据路径等。 + /// public class FlowDataDetails { /// @@ -83,11 +86,12 @@ namespace Serein.Workbench.Themes // 用于存储当前展开的节点路径 private HashSet expandedNodePaths = new HashSet(); - + /// /// 加载对象信息,展示其成员 /// + /// 与对象关联的Key,用于管理对象 /// 要展示的对象 public void LoadObjectInformation(string key, object obj) { @@ -113,7 +117,7 @@ namespace Serein.Workbench.Themes /// /// /// - private async void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e) + private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e) { //if (FlowEnvironment is not null && await FlowEnvironment.AddInterruptExpressionAsync(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式 //{ @@ -441,6 +445,7 @@ namespace Serein.Workbench.Themes /// /// /// + /// /// private string GetPropertyValue(object obj, PropertyInfo property,out object? value) { @@ -469,6 +474,7 @@ namespace Serein.Workbench.Themes /// /// /// + /// /// private string GetFieldValue(object obj, FieldInfo field, out object? value) { @@ -486,6 +492,7 @@ namespace Serein.Workbench.Themes } } +#if false /// @@ -651,3 +658,5 @@ namespace Serein.Workbench.Themes //} + +#endif \ No newline at end of file diff --git a/Workbench/Themes/TypeViewerWindow.xaml.cs b/Workbench/Themes/TypeViewerWindow.xaml.cs index a84d13e..6affdb1 100644 --- a/Workbench/Themes/TypeViewerWindow.xaml.cs +++ b/Workbench/Themes/TypeViewerWindow.xaml.cs @@ -157,7 +157,9 @@ namespace Serein.Workbench.Themes /// /// 设置子项节点的事件 /// + /// /// + /// /// private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member,out ContextMenu? contextMenu) { diff --git a/Workbench/Tool/GuidReplacer.cs b/Workbench/Tool/GuidReplacer.cs index eb2cf77..6008eb6 100644 --- a/Workbench/Tool/GuidReplacer.cs +++ b/Workbench/Tool/GuidReplacer.cs @@ -11,15 +11,20 @@ namespace Serein.Workbench.Tool /// public class GuidReplacer { + private class TrieNode { public Dictionary Children = new(); - public string Replacement; // 替换后的值 + public string Replacement = string.Empty; // 替换后的值 } private readonly TrieNode _root = new(); - // 构建字典树 + /// + /// 构建字典树 + /// + /// + /// public void AddReplacement(string guid, string replacement) { var current = _root; @@ -34,7 +39,11 @@ namespace Serein.Workbench.Tool current.Replacement = replacement; } - // 替换逻辑 + /// + /// 替换逻辑 + /// + /// + /// public string Replace(string input) { var result = new StringBuilder(); diff --git a/Workbench/ViewModels/FlowEditViewModel.cs b/Workbench/ViewModels/FlowEditViewModel.cs index 4fc1ad9..1081d54 100644 --- a/Workbench/ViewModels/FlowEditViewModel.cs +++ b/Workbench/ViewModels/FlowEditViewModel.cs @@ -20,7 +20,7 @@ namespace Serein.Workbench.ViewModels /// /// 流程编辑数据视图 /// - public partial class FlowEditViewModel : ObservableObject + internal partial class FlowEditViewModel : ObservableObject { /// /// 画布集合 diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs index e519854..59da142 100644 --- a/Workbench/ViewModels/MainMenuBarViewModel.cs +++ b/Workbench/ViewModels/MainMenuBarViewModel.cs @@ -8,7 +8,7 @@ using System.Windows.Input; namespace Serein.Workbench.ViewModels { - public class MainMenuBarViewModel : ObservableObject + internal class MainMenuBarViewModel : ObservableObject { private readonly IFlowEnvironment flowEnvironment; private readonly FlowNodeService flowNodeService; diff --git a/Workbench/ViewModels/MainViewModel.cs b/Workbench/ViewModels/MainViewModel.cs index 2391529..3d93f37 100644 --- a/Workbench/ViewModels/MainViewModel.cs +++ b/Workbench/ViewModels/MainViewModel.cs @@ -10,10 +10,17 @@ using System.Windows; namespace Serein.Workbench.ViewModels { + /// + /// 主视图模型 + /// public class MainViewModel : ObservableObject { private readonly IKeyEventService keyEventService; + /// + /// 主视图模型构造函数 + /// + /// public MainViewModel(IKeyEventService keyEventService) { diff --git a/Workbench/Views/BaseNodesView.xaml.cs b/Workbench/Views/BaseNodesView.xaml.cs index a4423c0..ae54594 100644 --- a/Workbench/Views/BaseNodesView.xaml.cs +++ b/Workbench/Views/BaseNodesView.xaml.cs @@ -23,6 +23,9 @@ namespace Serein.Workbench.Views /// public partial class BaseNodesView : UserControl { + /// + /// 基础节点视图构造函数 + /// public BaseNodesView() { this.DataContext = App.GetService().BaseNodesViewModel; diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs index d81ae86..224800a 100644 --- a/Workbench/Views/FlowCanvasView.xaml.cs +++ b/Workbench/Views/FlowCanvasView.xaml.cs @@ -74,10 +74,6 @@ namespace Serein.Workbench.Views /// private bool IsSelectControl; /// - /// 标记是否正在进行连接操作 - /// - //private bool IsConnecting; - /// /// 标记是否正在拖动控件 /// private bool IsControlDragging; @@ -124,6 +120,10 @@ namespace Serein.Workbench.Views #region 初始化画布与相关事件 + /// + /// FlowCanvasView 构造函数 + /// + /// public FlowCanvasView(FlowCanvasDetails model) { var vm = App.GetService().FlowCanvasViewModel; @@ -615,7 +615,7 @@ namespace Serein.Workbench.Views if (key == Key.V && (keyEventService.GetKeyState(Key.LeftCtrl) || keyEventService.GetKeyState(Key.RightCtrl))) { string clipboardText = Clipboard.GetText(TextDataFormat.Text); // 获取复制的文本 - string nodesText = ""; + string? nodesText = ""; try { var jobject = JObject.Parse(clipboardText); @@ -623,6 +623,7 @@ namespace Serein.Workbench.Views } catch (Exception ex) { + SereinEnv.WriteLine(InfoType.ERROR, $"粘贴节点信息失败: {ex.Message}"); return; } @@ -878,7 +879,7 @@ namespace Serein.Workbench.Views /// /// /// - private async void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (IsSelectControl) { @@ -895,7 +896,7 @@ namespace Serein.Workbench.Views // 创建连线 var cd = flowNodeService.ConnectingData; - if (cd.IsCreateing) + if (cd.IsCreateing && cd.CurrentJunction is not null) { if (cd.IsCanConnected) { @@ -1593,6 +1594,12 @@ namespace Serein.Workbench.Views #region Plan A 群组对齐 + /// + /// 对齐选中的控件,支持多种对齐方式。 + /// + /// + /// + /// public void AlignControlsWithGrouping(List selectNodeControls, double proximityThreshold = 50, double spacing = 10) { if (selectNodeControls is null || selectNodeControls.Count < 2) @@ -1673,6 +1680,11 @@ namespace Serein.Workbench.Views #endregion #region Plan B 规划对齐 + /// + /// 使用动态规划对齐选中的控件,确保控件之间有间距,并且不重叠。 + /// + /// + /// public void AlignControlsWithDynamicProgramming(List selectNodeControls, double spacing = 10) { if (selectNodeControls is null || selectNodeControls.Count < 2) @@ -1751,6 +1763,10 @@ namespace Serein.Workbench.Views #endregion + + /// + /// 对齐模式枚举 + /// public enum AlignMode { /// @@ -1780,7 +1796,13 @@ namespace Serein.Workbench.Views Grouping, } - + /// + /// 对齐控件,支持多种对齐方式。 + /// + /// + /// + /// + /// public void AlignControlsWithGrouping(List selectNodeControls, AlignMode alignMode, double proximityThreshold = 50, double spacing = 10) { if (selectNodeControls is null || selectNodeControls.Count < 2) diff --git a/Workbench/Views/FlowEditView.xaml.cs b/Workbench/Views/FlowEditView.xaml.cs index 3f7c17f..d27d9e4 100644 --- a/Workbench/Views/FlowEditView.xaml.cs +++ b/Workbench/Views/FlowEditView.xaml.cs @@ -31,7 +31,9 @@ namespace Serein.Workbench.Views /// public partial class FlowEditView : UserControl { - + /// + /// 流程编辑视图构造函数 + /// public FlowEditView() { this.DataContext = App.GetService().FlowEditViewModel; diff --git a/Workbench/Views/FlowLibrarysView.xaml.cs b/Workbench/Views/FlowLibrarysView.xaml.cs index a21141a..cab66ba 100644 --- a/Workbench/Views/FlowLibrarysView.xaml.cs +++ b/Workbench/Views/FlowLibrarysView.xaml.cs @@ -23,6 +23,10 @@ namespace Serein.Workbench.Views public partial class FlowLibrarysView : UserControl { private FlowLibrarysViewModel ViewModel => DataContext as FlowLibrarysViewModel ?? throw new ArgumentNullException(); + + /// + /// FlowLibrarysView 构造函数 + /// public FlowLibrarysView() { this.DataContext = App.GetService().FlowLibrarysViewModel; diff --git a/Workbench/Views/FlowWorkbenchView.xaml.cs b/Workbench/Views/FlowWorkbenchView.xaml.cs index fd4af08..501216b 100644 --- a/Workbench/Views/FlowWorkbenchView.xaml.cs +++ b/Workbench/Views/FlowWorkbenchView.xaml.cs @@ -21,6 +21,10 @@ namespace Serein.Workbench.Views public partial class FlowWorkbenchView : Window { private FlowWorkbenchViewModel ViewModel => ViewModel as FlowWorkbenchViewModel; + + /// + /// FlowWorkbenchView 的交互逻辑 + /// public FlowWorkbenchView() { this.DataContext = App.GetService().FlowWorkbenchViewModel; @@ -49,6 +53,10 @@ namespace Serein.Workbench.Views System.Windows.Application.Current.Shutdown(); } + /// + /// 处理鼠标按下事件,确保点击空白区域时清除焦点 + /// + /// protected override void OnPreviewMouseDown(MouseButtonEventArgs e) { // 获取当前的焦点控件 diff --git a/Workbench/Views/MainMenuBarView.xaml.cs b/Workbench/Views/MainMenuBarView.xaml.cs index 2739575..496168d 100644 --- a/Workbench/Views/MainMenuBarView.xaml.cs +++ b/Workbench/Views/MainMenuBarView.xaml.cs @@ -21,6 +21,9 @@ namespace Serein.Workbench.Views /// public partial class MainMenuBarView : UserControl { + /// + /// MainMenuBarView 的构造函数 + /// public MainMenuBarView() { this.DataContext = App.GetService().MainMenuBarViewModel; diff --git a/Workbench/Views/MainView.xaml.cs b/Workbench/Views/MainView.xaml.cs index c04422c..1a16630 100644 --- a/Workbench/Views/MainView.xaml.cs +++ b/Workbench/Views/MainView.xaml.cs @@ -21,6 +21,9 @@ namespace Serein.Workbench.Views /// public partial class MainView : UserControl { + /// + /// MainView 的构造函数 + /// public MainView() { this.DataContext = App.GetService().MainViewModel; diff --git a/Workbench/Views/ViewCanvasInfoView.xaml.cs b/Workbench/Views/ViewCanvasInfoView.xaml.cs index 739067b..259acef 100644 --- a/Workbench/Views/ViewCanvasInfoView.xaml.cs +++ b/Workbench/Views/ViewCanvasInfoView.xaml.cs @@ -26,6 +26,10 @@ namespace Serein.Workbench.Views { private readonly ViewCanvasInfoViewModel ViewModel; private readonly ViewNodeInfoViewModel NodeInfoViewModel; + + /// + /// 画布信息查看视图 + /// public ViewCanvasInfoView() { this.ViewModel = App.GetService(); diff --git a/Workbench/Views/ViewNodeInfoView.xaml.cs b/Workbench/Views/ViewNodeInfoView.xaml.cs index 9518706..07cacfa 100644 --- a/Workbench/Views/ViewNodeInfoView.xaml.cs +++ b/Workbench/Views/ViewNodeInfoView.xaml.cs @@ -22,6 +22,10 @@ namespace Serein.Workbench.Views public partial class ViewNodeInfoView : UserControl { private readonly ViewNodeInfoViewModel ViewModel; + + /// + /// ViewNodeInfoView 的交互逻辑 + /// public ViewNodeInfoView() { diff --git a/Workbench/Views/ViewNodeMethodInfoView.xaml.cs b/Workbench/Views/ViewNodeMethodInfoView.xaml.cs index c64533b..f4b4386 100644 --- a/Workbench/Views/ViewNodeMethodInfoView.xaml.cs +++ b/Workbench/Views/ViewNodeMethodInfoView.xaml.cs @@ -23,6 +23,9 @@ namespace Serein.Workbench.Views { private readonly ViewNodeMethodInfoViewModel ViewModel; + /// + /// ViewNodeMethodInfoView 的交互逻辑 + /// public ViewNodeMethodInfoView() { ViewModel = App.GetService().ViewNodeMethodInfoViewModel;