From 1ae4c87aac042fb57a0e5c2d6f99c694cf876fe0 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Sat, 15 Mar 2025 15:43:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8EDLL=E5=AF=BC=E5=85=A5=E6=96=B9?= =?UTF-8?q?=E6=B3=95=E5=B0=86=E6=8C=89=E7=85=A7=E6=96=B9=E6=B3=95=E5=90=8D?= =?UTF-8?q?=E7=A7=B0=E9=A6=96=E5=AD=97=E6=AF=8D=E5=BC=80=E5=A7=8B=E6=8E=92?= =?UTF-8?q?=E5=BA=8F=EF=BC=9B=E8=A7=84=E8=8C=83=E5=8C=96=E6=96=B9=E6=B3=95?= =?UTF-8?q?=E5=8F=82=E6=95=B0=E6=8F=8F=E8=BF=B0=E4=B8=AD=E7=9A=84=E8=BE=93?= =?UTF-8?q?=E5=85=A5=E7=B1=BB=E5=9E=8B=EF=BC=8C=E6=94=B9=E4=B8=BA=E6=9E=9A?= =?UTF-8?q?=E4=B8=BE=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Enums/ParameterValueInputType.cs | 14 +++ Library/FlowNode/NodeModelBaseFunc.cs | 2 +- Library/FlowNode/ParameterDetails.cs | 19 ++- Library/FlowNode/ParameterDetailsInfo.cs | 2 +- NodeFlow/Model/SingleConditionNode.cs | 2 +- NodeFlow/Model/SingleExpOpNode.cs | 2 +- NodeFlow/Model/SingleScriptNode.cs | 2 +- NodeFlow/Tool/FlowLibrary.cs | 37 ++++-- NodeFlow/Tool/FlowLibraryManagement.cs | 22 +++- NodeFlow/Tool/NodeMethodDetailsHelper.cs | 108 +++++++++++++----- .../IsVisibleOfParameterConverter.cs | 4 +- .../ViewModels/ParameterDetailsViewModel.cs | 2 +- Workbench/Node/View/ScriptNodeControl.xaml.cs | 2 +- Workbench/Themes/MethodDetailsControl.xaml | 4 +- 14 files changed, 165 insertions(+), 57 deletions(-) create mode 100644 Library/Enums/ParameterValueInputType.cs diff --git a/Library/Enums/ParameterValueInputType.cs b/Library/Enums/ParameterValueInputType.cs new file mode 100644 index 0000000..7cee072 --- /dev/null +++ b/Library/Enums/ParameterValueInputType.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library +{ + public enum ParameterValueInputType + { + Input, + Select, + } +} diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index e7a8020..6e63eb2 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -74,7 +74,7 @@ namespace Serein.Library pd.DataType = null; pd.Name = null; pd.ArgDataSourceNodeGuid = null; - pd.ExplicitTypeName = null; + pd.InputType = ParameterValueInputType.Input; } this.MethodDetails.ParameterDetailss = null; this.MethodDetails.ActingInstance = null; diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs index 9978424..95b7044 100644 --- a/Library/FlowNode/ParameterDetails.cs +++ b/Library/FlowNode/ParameterDetails.cs @@ -10,6 +10,8 @@ using System.Threading.Tasks; namespace Serein.Library { + + /// /// 节点入参参数详情 /// @@ -57,7 +59,7 @@ namespace Serein.Library /// Value :除以上类型之外的任意参数 /// [PropertyInfo] - private string _explicitTypeName ; + private ParameterValueInputType _inputType ; /// /// 入参数据来源。默认使用上一节点作为入参数据。 @@ -91,7 +93,7 @@ namespace Serein.Library private string _dataValue; /// - /// 只有当ExplicitTypeName 为 Select 时,才会需要该成员。 + /// 只有当 InputType 为 Select 时,才会需要该成员。 /// [PropertyInfo(IsNotification = true)] private string[] _items ; @@ -104,6 +106,8 @@ namespace Serein.Library } + + public partial class ParameterDetails { @@ -133,7 +137,7 @@ namespace Serein.Library Name = info.Name; DataType = Type.GetType(info.DataTypeFullName); ExplicitType = Type.GetType(info.ExplicitTypeFullName); - ExplicitTypeName = info.ExplicitTypeName; + InputType = info.InputType.ConvertEnum(); Items = info.Items; IsParams = info.IsParams; } @@ -151,7 +155,7 @@ namespace Serein.Library DataTypeFullName = this.DataType.FullName, Name = this.Name, ExplicitTypeFullName = this.ExplicitType.FullName, - ExplicitTypeName = this.ExplicitTypeName, + InputType = this.InputType.ToString(), Items = this.Items.Select(it => it).ToArray(), }; } @@ -168,7 +172,7 @@ namespace Serein.Library Index = this.Index, IsExplicitData = this.IsExplicitData, ExplicitType = this.ExplicitType, - ExplicitTypeName = this.ExplicitTypeName, + InputType = this.InputType, //Convertor = this.Convertor, DataType = this.DataType, Name = this.Name, @@ -199,6 +203,11 @@ namespace Serein.Library { return context; } + // 返回流程上下文 + if (typeof(NodeModelBase).IsAssignableFrom(DataType)) + { + return NodeModel; + } // 显式设置的参数 if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase)) { diff --git a/Library/FlowNode/ParameterDetailsInfo.cs b/Library/FlowNode/ParameterDetailsInfo.cs index 4fc6905..869b7a7 100644 --- a/Library/FlowNode/ParameterDetailsInfo.cs +++ b/Library/FlowNode/ParameterDetailsInfo.cs @@ -43,7 +43,7 @@ namespace Serein.Library /// Bool : 布尔类型 /// Value : 除以上类型之外的任意参数 /// - public string ExplicitTypeName { get; set; } + public string InputType { get; set; } /// /// 参数选择器 diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index d5836e7..ae41a6f 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -71,7 +71,7 @@ namespace Serein.NodeFlow.Model ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, NodeModel = this, //Convertor = null, - ExplicitTypeName = "Value", + InputType = ParameterValueInputType.Input, Items = null, }; this.MethodDetails.ParameterDetailss = [..pd]; diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs index 2098862..302cd79 100644 --- a/NodeFlow/Model/SingleExpOpNode.cs +++ b/NodeFlow/Model/SingleExpOpNode.cs @@ -60,7 +60,7 @@ namespace Serein.NodeFlow.Model ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, NodeModel = this, //Convertor = null, - ExplicitTypeName = "Value", + InputType = ParameterValueInputType.Input, Items = null, }; this.MethodDetails.ParameterDetailss = [.. pd]; diff --git a/NodeFlow/Model/SingleScriptNode.cs b/NodeFlow/Model/SingleScriptNode.cs index 38b72a2..4af4159 100644 --- a/NodeFlow/Model/SingleScriptNode.cs +++ b/NodeFlow/Model/SingleScriptNode.cs @@ -89,7 +89,7 @@ namespace Serein.NodeFlow.Model ArgDataSourceNodeGuid = string.Empty, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, NodeModel = this, - ExplicitTypeName = "Value", + InputType = ParameterValueInputType.Input, Items = null, IsParams = true, }; diff --git a/NodeFlow/Tool/FlowLibrary.cs b/NodeFlow/Tool/FlowLibrary.cs index e27ed58..35c4bb9 100644 --- a/NodeFlow/Tool/FlowLibrary.cs +++ b/NodeFlow/Tool/FlowLibrary.cs @@ -5,13 +5,21 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Text; +using static System.Runtime.InteropServices.JavaScript.JSType; namespace Serein.NodeFlow { + public class LibraryMdDd (MethodDetails methodDetails,DelegateDetails delegateDetails) + { + public MethodDetails MethodDetails { get; } = methodDetails; + public DelegateDetails DelegateDetails { get; } = delegateDetails; + } + /// /// 加载在流程中的程序集依赖 @@ -111,7 +119,7 @@ namespace Serein.NodeFlow var loaderExceptions = ex.LoaderExceptions; foreach (var loaderException in loaderExceptions) { - SereinEnv.WriteLine(InfoType.ERROR, loaderException.Message); + SereinEnv.WriteLine(InfoType.ERROR, loaderException?.Message); } return false; } @@ -149,7 +157,7 @@ namespace Serein.NodeFlow // 从 scanTypes.Type 创建的方法信息 // Md : 方法描述 // Dd :方法对应的Emit委托 - List<(MethodDetails Md, DelegateDetails Dd)> detailss = new List<(MethodDetails Md, DelegateDetails Dd)>(); + List detailss = new List(); // 遍历扫描的类型 foreach ((var type, var flowName) in scanTypes) @@ -165,7 +173,7 @@ namespace Serein.NodeFlow continue; } md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名 - detailss.Add((md, dd)); + detailss.Add(new LibraryMdDd(md, dd)); } } @@ -176,11 +184,26 @@ namespace Serein.NodeFlow { return false; } - #region 加载成功,缓存所有方法、委托的信息 - foreach ((var md, var dd) in detailss) + // 简单排序一下 + //detailss = detailss.OrderBy(k => k.MethodDetails.MethodName,).ToList(); + + + + detailss.Sort((a, b) => string.Compare(a.MethodDetails.MethodName, b.MethodDetails.MethodName, StringComparison.OrdinalIgnoreCase)); + foreach (var item in detailss) { - MethodDetailss.TryAdd(md.MethodName, md); - DelegateDetailss.TryAdd(md.MethodName, dd); + SereinEnv.WriteLine(InfoType.INFO, "loading method : " + item.MethodDetails.MethodName); + + } + + //detailss.Sort((a, b) => string.Compare()); + + #region 加载成功,缓存所有方法、委托的信息 + foreach (var item in detailss) + { + var key = item.MethodDetails.MethodName; + MethodDetailss.TryAdd(key, item.MethodDetails); + DelegateDetailss.TryAdd(key, item.DelegateDetails); } #endregion diff --git a/NodeFlow/Tool/FlowLibraryManagement.cs b/NodeFlow/Tool/FlowLibraryManagement.cs index c78e21e..6326c9d 100644 --- a/NodeFlow/Tool/FlowLibraryManagement.cs +++ b/NodeFlow/Tool/FlowLibraryManagement.cs @@ -229,7 +229,8 @@ namespace Serein.NodeFlow.Tool GC.WaitForPendingFinalizers(); }; var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集 - return LoadAssembly(assembly, actionUnload); + var assembly_result = LoadAssembly(assembly, actionUnload); + return assembly_result; } /* var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径 @@ -272,15 +273,26 @@ namespace Serein.NodeFlow.Tool } FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload); - if (flowLibrary.LoadAssembly(assembly)) + var loadResult = flowLibrary.LoadAssembly(assembly); // 加载程序集 + if (loadResult) { - _myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary); - (NodeLibraryInfo, List) result = (flowLibrary.ToInfo(), - flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList()); + var assemblyName = assembly.GetName().Name; + if (string.IsNullOrEmpty(assemblyName)) + { + actionUnload.Invoke(); + throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败,没有程序集名称"); + } + _myFlowLibrarys.TryAdd(assemblyName, flowLibrary); + + List mdInfos = flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList(); + mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase)); + + (NodeLibraryInfo, List) result = (flowLibrary.ToInfo(), mdInfos); return result; } else { + actionUnload.Invoke(); throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败"); } } diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs index 8dcf030..646d3ac 100644 --- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs +++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs @@ -6,6 +6,7 @@ using System.Reflection; using Serein.Library.FlowNode; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.ComponentModel.DataAnnotations; namespace Serein.NodeFlow.Tool; @@ -47,13 +48,10 @@ public static class NodeMethodDetailsHelper } var methodName = $"{assemblyName}.{type.Name}.{methodInfo.Name}"; - SereinEnv.WriteLine(InfoType.INFO, "loading method : " + methodName); - + // 创建参数信息 var explicitDataOfParameters = GetExplicitDataOfParameters(methodInfo.GetParameters()); - - //// 通过表达式树生成委托 //var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 // method, // 方法信息 @@ -65,6 +63,34 @@ public static class NodeMethodDetailsHelper Type? returnType; bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult); + + if (attribute.MethodDynamicType == Library.NodeType.UI) + { + if (isTask) + { + var innerType = methodInfo.ReturnType.GetGenericArguments()[0]; + if (innerType.IsGenericType && innerType != typeof(IEmbeddedContent)) + { + SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为UI方法的返回值并非IEmbeddedContent,流程工作台将无法正确显示自定义控件界面以及传递数据。"); + methodDetails = null; + delegateDetails = null; + return false; + } + } + else + { + if (methodInfo.ReturnType != typeof(IEmbeddedContent)) + { + SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为UI方法的返回值并非IEmbeddedContent,流程工作台将无法正确显示自定义控件界面以及传递数据。"); + methodDetails = null; + delegateDetails = null; + return false; + } + } + + } + + // 对于触发器 if (attribute.MethodDynamicType == Library.NodeType.Flipflop) { if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) @@ -91,13 +117,8 @@ public static class NodeMethodDetailsHelper delegateDetails = null; return false; } - - //if (!isTask || taskResult != typeof(IFlipflopContext)) - //{ - // - //} - } + else if(isTask) { returnType = taskResult is null ? typeof(Task) : taskResult; @@ -111,8 +132,6 @@ public static class NodeMethodDetailsHelper attribute.AnotherName = methodInfo.Name; } - - var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ; var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName; @@ -126,7 +145,7 @@ public static class NodeMethodDetailsHelper { ActingInstanceType = type, // ActingInstance = instance, - MethodName = methodName, + MethodName = NodeMethodDetailsHelper.GetMethodSignature(methodInfo), AssemblyName = assemblyName, MethodDynamicType = attribute.MethodDynamicType, MethodLockName = attribute.LockName, @@ -147,7 +166,17 @@ public static class NodeMethodDetailsHelper return true; } + public static string GetMethodSignature(MethodInfo method) + { + if (method == null) return string.Empty; + string methodName = method.Name; + string returnType = method.ReturnType.Name; + string parameters = string.Join(", ", method.GetParameters() + .Select(p => $"{p.ParameterType.Name} {p.Name}")); + + return $"{methodName}({parameters}) : {returnType}"; + } public static bool IsGenericTask(Type returnType, out Type? taskResult) { // 判断是否为 Task 类型或泛型 Task @@ -236,6 +265,27 @@ public static class NodeMethodDetailsHelper #endregion }).ToArray(); + foreach(var pd in tempParams) + { + var argType = pd.DataType; + // 运行环境 + if (typeof(IFlowEnvironment).IsAssignableFrom(argType)) + { + continue; + } + // 流程上下文 + if (typeof(IDynamicContext).IsAssignableFrom(argType)) + { + continue; + } + // 节点模型 + if (typeof(NodeModelBase).IsAssignableFrom(argType)) + { + continue; + } + pd.IsExplicitData = true; + + } return tempParams; } @@ -260,14 +310,14 @@ public static class NodeMethodDetailsHelper dataType = parameterInfo.ParameterType; } - string explicitTypeName = GetExplicitTypeName(explicitParemType); - var items = GetExplicitItems(explicitParemType, explicitTypeName); - if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型 + var inputType = GetInputType(explicitParemType); + var items = GetExplicitItems(explicitParemType, inputType); + //if ("Bool".Equals(inputType)) inputType = "Select"; // 布尔值 转为 可选类型 return new ParameterDetails { IsExplicitData = isExplicitData, //attribute is null ? parameterInfo.HasDefaultValue : true, Index = index, // 索引 - ExplicitTypeName = explicitTypeName, // Select/Bool/Value + InputType = inputType, // Select/Bool/Value ExplicitType = explicitParemType,// 显示的入参类型 //Convertor = func, // 转换器 DataType = dataType, // 实际的入参类型 @@ -286,17 +336,17 @@ public static class NodeMethodDetailsHelper /// /// /// - private static string GetExplicitTypeName(Type type) + private static ParameterValueInputType GetInputType(Type type) { return type switch { - Type t when t.IsEnum => "Select", - Type t when t == typeof(bool) => "Bool", - Type t when t == typeof(string) => "Value", - Type t when t == typeof(int) => "Value", - Type t when t == typeof(double) => "Value", - _ => "Value" + Type t when t.IsEnum => ParameterValueInputType.Select, + Type t when t == typeof(bool) => ParameterValueInputType.Select, + Type t when t == typeof(string) => ParameterValueInputType.Input, + Type t when t == typeof(int) => ParameterValueInputType.Input, + Type t when t == typeof(double) => ParameterValueInputType.Input, + _ => ParameterValueInputType.Input }; } @@ -305,14 +355,14 @@ public static class NodeMethodDetailsHelper /// 获取参数列表选项 /// /// - /// + /// /// - private static IEnumerable GetExplicitItems(Type type, string explicitTypeName) + private static IEnumerable GetExplicitItems(Type type, ParameterValueInputType InputType) { - IEnumerable items = explicitTypeName switch + IEnumerable items = InputType switch { - "Select" => Enum.GetNames(type), - "Bool" => ["True", "False"], + ParameterValueInputType.Select when type == typeof(bool) => ["True", "False"], + ParameterValueInputType.Select => Enum.GetNames(type), _ => [] }; return items; diff --git a/Serein.Workbench.Avalonia/Converters/IsVisibleOfParameterConverter.cs b/Serein.Workbench.Avalonia/Converters/IsVisibleOfParameterConverter.cs index f6e7309..47d20a8 100644 --- a/Serein.Workbench.Avalonia/Converters/IsVisibleOfParameterConverter.cs +++ b/Serein.Workbench.Avalonia/Converters/IsVisibleOfParameterConverter.cs @@ -17,8 +17,8 @@ namespace Serein.Workbench.Avalonia.Converters if(value is ParameterDetails pd) { - - if (pd.ExplicitTypeName == "Value") + + if (pd.InputType == ParameterValueInputType.Input) { return false; diff --git a/Serein.Workbench.Avalonia/Custom/ViewModels/ParameterDetailsViewModel.cs b/Serein.Workbench.Avalonia/Custom/ViewModels/ParameterDetailsViewModel.cs index 779caab..571b5c9 100644 --- a/Serein.Workbench.Avalonia/Custom/ViewModels/ParameterDetailsViewModel.cs +++ b/Serein.Workbench.Avalonia/Custom/ViewModels/ParameterDetailsViewModel.cs @@ -48,7 +48,7 @@ namespace Serein.Workbench.Avalonia.Custom.ViewModels return; } - if ("Value".Equals(ParameterDetails.ExplicitTypeName)) + if (ParameterDetails.InputType == ParameterValueInputType.Input ) { // 值类型 IsVisibleA = false; diff --git a/Workbench/Node/View/ScriptNodeControl.xaml.cs b/Workbench/Node/View/ScriptNodeControl.xaml.cs index f16decf..7a3f1d0 100644 --- a/Workbench/Node/View/ScriptNodeControl.xaml.cs +++ b/Workbench/Node/View/ScriptNodeControl.xaml.cs @@ -72,7 +72,7 @@ namespace Serein.Workbench.Node.View // 获取 MethodDetailsControl 实例 var methodDetailsControl = this.MethodDetailsControl; var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl - if (itemsControl != null) + if (itemsControl != null && base.ViewModel.NodeModel.MethodDetails.ParameterDetailss != null) { var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length]; var controls = new List(); diff --git a/Workbench/Themes/MethodDetailsControl.xaml b/Workbench/Themes/MethodDetailsControl.xaml index ce11b2a..9fda70f 100644 --- a/Workbench/Themes/MethodDetailsControl.xaml +++ b/Workbench/Themes/MethodDetailsControl.xaml @@ -137,7 +137,7 @@ - + @@ -160,7 +160,7 @@ - +