From 0471452ed966cea17c6914d99c020df230b81137 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Wed, 18 Dec 2024 00:05:42 +0800 Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=96=B0=E8=AE=BE=E8=AE=A1=E4=BA=86@g?= =?UTF-8?q?et=20=E8=A1=A8=E8=BE=BE=E5=BC=8F=EF=BC=8C=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E4=BA=86=20Workbench=E5=90=8E=E5=8F=B0=E9=95=BF=E6=97=B6?= =?UTF-8?q?=E9=97=B4=E8=BF=90=E8=A1=8C=E6=97=B6=EF=BC=8C=E9=87=8D=E6=96=B0?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E5=88=B0=E5=89=8D=E5=8F=B0=E4=BC=9A=E4=BA=A7?= =?UTF-8?q?=E7=94=9F=E5=8F=82=E6=95=B0=E8=BF=9E=E6=8E=A5=E7=BA=BF=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E6=98=BE=E7=A4=BA=E7=9A=84=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Extension/SereinExtension.cs | 107 +++++++- Library/FlowNode/NodeModelBaseFunc.cs | 18 +- Library/SereinBaseFunction.cs | 6 + Library/Utils/ConvertHelper.cs | 11 + .../Resolver/BoolConditionResolver.cs | 2 +- .../SereinExpression/SereinConditionParser.cs | 154 +----------- .../SereinExpressionExtension.cs | 57 +++++ .../SerinExpressionEvaluator.cs | 234 +++++++----------- NodeFlow/FlowStarter.cs | 19 ++ NodeFlow/Model/SingleConditionNode.cs | 8 +- WorkBench/App.xaml.cs | 43 +++- WorkBench/MainWindow.xaml.cs | 3 +- Workbench/Node/INodeJunction.cs | 10 + .../Node/Junction/ConnectionLineShape.cs | 2 + .../Node/Junction/JunctionControlBase.cs | 2 +- Workbench/Node/View/ConnectionControl.cs | 16 +- 16 files changed, 386 insertions(+), 306 deletions(-) create mode 100644 Library/Utils/SereinExpression/SereinExpressionExtension.cs diff --git a/Library/Extension/SereinExtension.cs b/Library/Extension/SereinExtension.cs index 84e1c95..83fd8df 100644 --- a/Library/Extension/SereinExtension.cs +++ b/Library/Extension/SereinExtension.cs @@ -1,5 +1,7 @@ -using System; +using Serein.Library.Utils; +using System; using System.Collections.Generic; +using System.Data.SqlTypes; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -11,6 +13,109 @@ namespace Serein.Library /// public static partial class SereinExtension { + /// + /// 字面量转为对应类型 + /// + /// + /// + public static Type ToTypeOfString(this string valueStr) + { + if (valueStr.IndexOf('.') != -1) + { + // 通过指定的类型名称获取类型 + return Type.GetType(valueStr); + } + + + if (valueStr.Equals("bool", StringComparison.OrdinalIgnoreCase)) + { + return typeof(bool); + } + #region 整数型 + else if (valueStr.Equals("sbyte", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(SByte), StringComparison.OrdinalIgnoreCase)) + { + return typeof(SByte); + } + else if (valueStr.Equals("short", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int16), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int16); + } + else if (valueStr.Equals("int", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int32), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int32); + } + else if (valueStr.Equals("long", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Int64), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Int64); + } + + else if (valueStr.Equals("byte", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Byte), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Byte); + } + else if (valueStr.Equals("ushort", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt16), StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt16); + } + else if (valueStr.Equals("uint", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt32), StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt32); + } + else if (valueStr.Equals("ulong", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(UInt64), StringComparison.OrdinalIgnoreCase)) + { + return typeof(UInt64); + } + #endregion + + #region 浮点型 + else if (valueStr.Equals("float", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Single), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Single); + } + else if (valueStr.Equals("double", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Double), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Double); + } + #endregion + + #region 小数型 + + else if (valueStr.Equals("decimal", StringComparison.OrdinalIgnoreCase) + || valueStr.Equals(nameof(Decimal), StringComparison.OrdinalIgnoreCase)) + { + return typeof(Decimal); + } + #endregion + + #region 其他常见的类型 + else if (valueStr.Equals(nameof(DateTime), StringComparison.OrdinalIgnoreCase)) + { + return typeof(DateTime); + } + + else if (valueStr.Equals(nameof(String), StringComparison.OrdinalIgnoreCase)) + { + return typeof(String); + } + #endregion + + else + { + throw new ArgumentException($"无法解析的字面量类型[{valueStr}]"); + } + } + + /// /// 判断连接类型 /// diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs index d8077a4..1a74be1 100644 --- a/Library/FlowNode/NodeModelBaseFunc.cs +++ b/Library/FlowNode/NodeModelBaseFunc.cs @@ -357,6 +357,10 @@ namespace Serein.Library /// 节点传回数据对象 public virtual async Task ExecutingAsync(IDynamicContext context) { + if(context.NextOrientation == ConnectionInvokeType.IsError) + { + Console.WriteLine(""); + } #region 调试中断 if (DebugSetting.IsInterrupt) // 执行触发检查是否需要中断 @@ -468,7 +472,6 @@ namespace Serein.Library else { inputParameter = context.GetFlowData(previousNode.Guid); // 当前传递的数据 - } } #endregion @@ -485,8 +488,17 @@ namespace Serein.Library else if (pd.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke) { // 立刻调用对应节点获取数据。 - var result = await context.Env.InvokeNodeAsync(context, pd.ArgDataSourceNodeGuid); - inputParameter = result; + try + { + var result = await context.Env.InvokeNodeAsync(context, pd.ArgDataSourceNodeGuid); + inputParameter = result; + } + catch (Exception ex) + { + context.NextOrientation = ConnectionInvokeType.IsError; + context.ExceptionOfRuning = ex; + throw; + } } #endregion #region 意料之外的参数 diff --git a/Library/SereinBaseFunction.cs b/Library/SereinBaseFunction.cs index 5ca2937..3237df8 100644 --- a/Library/SereinBaseFunction.cs +++ b/Library/SereinBaseFunction.cs @@ -89,6 +89,12 @@ namespace Serein.Library return value; } + [NodeAction(NodeType.Action, "逻辑分支")] + private object SereinLogicalBranch(bool @bool, object t_value,object f_value) + { + return @bool ? t_value : f_value; + } + [NodeAction(NodeType.Action, "文本拼接")] private string SereinTextJoin(params object[] value) { diff --git a/Library/Utils/ConvertHelper.cs b/Library/Utils/ConvertHelper.cs index e8db44d..85e51a2 100644 --- a/Library/Utils/ConvertHelper.cs +++ b/Library/Utils/ConvertHelper.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; @@ -12,6 +13,8 @@ namespace Serein.Library.Utils /// public static class ConvertHelper { + + public static TResult ToConvert(this object data) { var type = typeof(TResult); @@ -203,6 +206,14 @@ namespace Serein.Library.Utils #endif else if(type == typeof(DateTime)) { + if (valueStr.Equals("now")) + { + return DateTime.Now; + } + else if (valueStr.Equals("utcnow")) + { + return DateTime.UtcNow; + } return DateTime.Parse(valueStr); } else diff --git a/Library/Utils/SereinExpression/Resolver/BoolConditionResolver.cs b/Library/Utils/SereinExpression/Resolver/BoolConditionResolver.cs index 7a2c5c2..19ba407 100644 --- a/Library/Utils/SereinExpression/Resolver/BoolConditionResolver.cs +++ b/Library/Utils/SereinExpression/Resolver/BoolConditionResolver.cs @@ -23,7 +23,7 @@ namespace Serein.Library.Utils.SereinExpression.Resolver public override bool Evaluate(object obj) { - return Value.Equals(Data); + return Value.Equals(obj); //if (obj is bool boolObj && Value is bool boolValue) //{ diff --git a/Library/Utils/SereinExpression/SereinConditionParser.cs b/Library/Utils/SereinExpression/SereinConditionParser.cs index c108a97..f0c7963 100644 --- a/Library/Utils/SereinExpression/SereinConditionParser.cs +++ b/Library/Utils/SereinExpression/SereinConditionParser.cs @@ -81,7 +81,7 @@ namespace Serein.Library.Utils.SereinExpression public static SereinConditionResolver ConditionParse(object data, string expression) { - if (expression.StartsWith(".")) // 表达式前缀属于从上一个节点数据对象获取成员值 + if (expression[0] == '.') // 表达式前缀属于从上一个节点数据对象获取成员值 { return ParseObjectExpression(data, expression); } @@ -190,61 +190,7 @@ namespace Serein.Library.Utils.SereinExpression operatorStr = parts[0].ToLower(); // 操作类型 valueStr = string.Join(" ", parts.Skip(1)); // 表达式值 } - Type tempType; - - switch (typeStr) - { - case "bool": - tempType = typeof(bool); - break; - case "float": - tempType = typeof(float); - break; - case "decimal": - tempType = typeof(decimal); - break; - case "double": - tempType = typeof(double); - break; - case "sbyte": - tempType = typeof(sbyte); - break; - case "byte": - tempType = typeof(byte); - break; - case "short": - tempType = typeof(short); - break; - case "ushort": - tempType = typeof(ushort); - break; - case "int": - tempType = typeof(int); - break; - case "uint": - tempType = typeof(uint); - break; - case "long": - tempType = typeof(long); - break; - case "ulong": - tempType = typeof(ulong); - break; - // 如果需要支持 nint 和 nuint - // case "nint": - // tempType = typeof(nint); - // break; - // case "nuint": - // tempType = typeof(nuint); - // break; - case "string": - tempType = typeof(string); - break; - default: - tempType = Type.GetType(typeStr); - break; - } - type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明"); + type = typeStr.ToTypeOfString(); if (string.IsNullOrWhiteSpace(memberPath)) { targetObj = Convert.ChangeType(data, type); @@ -362,32 +308,6 @@ namespace Serein.Library.Utils.SereinExpression /// private static SereinConditionResolver ParseSimpleExpression(object data, string expression) { - if ("pass".Equals(expression.ToLower())) - { - return new PassConditionResolver - { - Op = PassConditionResolver.Operator.Pass, - }; - } - else - { - if ("not pass".Equals(expression.ToLower())) - { - return new PassConditionResolver - { - Op = PassConditionResolver.Operator.NotPass, - }; - } - if ("!pass".Equals(expression.ToLower())) - { - return new PassConditionResolver - { - Op = PassConditionResolver.Operator.NotPass, - }; - } - } - - var parts = expression.Split(' '); if (parts.Length < 2) @@ -395,11 +315,11 @@ namespace Serein.Library.Utils.SereinExpression string operatorStr; string valueStr; - Type type = null; // 尝试获取指定类型 - int typeStartIndex = expression.IndexOf('<'); - int typeEndIndex = expression.IndexOf('>'); - if (typeStartIndex + typeStartIndex == -2) + + var hasType = SereinExpressionExtension.TryGetType(expression, out _, out var type); + + if (!hasType) { // 如果不需要转为指定类型 operatorStr = parts[0]; @@ -410,72 +330,16 @@ namespace Serein.Library.Utils.SereinExpression valueStr = StringHelper.JoinStrings(parts, 1, parts.Length - 1, ' '); #endif - type = data.GetType(); } else {//string typeStr = parts[0]; - string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1) - .Trim().ToLower(); // 手动置顶的类型 - parts = expression.Substring(typeEndIndex + 1).Trim().Split(' '); + + int endIndex = expression.IndexOf('>'); + parts = expression.Substring(endIndex + 1).Split(' '); operatorStr = parts[0].ToLower(); // 操作类型 valueStr = string.Join(" ", parts.Skip(1)); // 表达式值 - - Type tempType; - - switch (typeStr) - { - case "bool": - tempType = typeof(bool); - break; - case "float": - tempType = typeof(float); - break; - case "decimal": - tempType = typeof(decimal); - break; - case "double": - tempType = typeof(double); - break; - case "sbyte": - tempType = typeof(sbyte); - break; - case "byte": - tempType = typeof(byte); - break; - case "short": - tempType = typeof(short); - break; - case "ushort": - tempType = typeof(ushort); - break; - case "int": - tempType = typeof(int); - break; - case "uint": - tempType = typeof(uint); - break; - case "long": - tempType = typeof(long); - break; - case "ulong": - tempType = typeof(ulong); - break; - // 如果需要支持 nint 和 nuint - // case "nint": - // tempType = typeof(nint); - // break; - // case "nuint": - // tempType = typeof(nuint); - // break; - case "string": - tempType = typeof(string); - break; - default: - tempType = Type.GetType(typeStr); - break; - } } diff --git a/Library/Utils/SereinExpression/SereinExpressionExtension.cs b/Library/Utils/SereinExpression/SereinExpressionExtension.cs new file mode 100644 index 0000000..d481520 --- /dev/null +++ b/Library/Utils/SereinExpression/SereinExpressionExtension.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library.Utils.SereinExpression +{ + internal class SereinExpressionExtension + { + /// + /// 尝试获取类型 + /// + /// + /// + /// + /// + public static bool TryGetType(string context, out string elementName , out Type type) + { + int startIndex = context.IndexOf('<'); + int endIndex = context.IndexOf('>'); + if (startIndex < 0 || endIndex < 0 || startIndex > endIndex) + { + type = null; + elementName = null; + return false; + } + elementName = context.Substring(0,startIndex); + type = context.Substring(startIndex + 1, endIndex - startIndex - 1).ToTypeOfString(); + return true; + + } + + /// + /// 尝试获取下标 + /// + /// + /// 文本形式的key/索引 + /// + public static bool TryGetIndex(string context,out string elementName, out string strIndexKey) + { + int startIndex = context.IndexOf('['); + int endIndex = context.IndexOf(']'); + if (startIndex < 0 || endIndex < 0 || startIndex > endIndex) + { + strIndexKey = null; + elementName = null; + return false; + } + + elementName = context.Substring(0,startIndex); + strIndexKey = context.Substring(startIndex + 1, endIndex - startIndex - 1); + return true; + + } + } +} diff --git a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs index 298a4f4..a072463 100644 --- a/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs +++ b/Library/Utils/SereinExpression/SerinExpressionEvaluator.cs @@ -5,6 +5,7 @@ using System.Data; using System.Diagnostics; using System.IO; using System.Linq; +using System.Linq.Expressions; using System.Net.Http.Headers; using System.Reflection; using System.Threading.Tasks; @@ -63,38 +64,48 @@ namespace Serein.Library.Utils.SereinExpression throw new ArgumentException("Invalid expression format."); } - var operation = parts[0].ToLower(); + var operation = parts[0]; var operand = parts[1][0] == '.' ? parts[1].Substring(1) : parts[1]; object result; isChange = false; + //if (operation == "@num") //{ // result = ComputedNumber(targetObJ, operand); //} - if (operation == "@call") - { - result = InvokeMethod(targetObJ, operand); - } - else if (operation == "@get") + + if (operation.Equals("@get",StringComparison.OrdinalIgnoreCase)) { isChange = true; + if (operand[0].Equals('#')) + { + // 存在全局变量表达式 + var strIndex = operand.IndexOf('#',1); + var globalDataKeyName = operand.Substring(1, strIndex - 1); + targetObJ = SereinEnv.GetFlowGlobalData(globalDataKeyName); + if(strIndex == operand.Length - 1) + { + return targetObJ; + } + operand = operand.Substring(strIndex+1); + } + result = GetMember(targetObJ, operand); } - else if (operation == "@set") - { - isChange = true; - result = SetMember(targetObJ, operand); - } - else if (operation == "@dtc") + else if (operation.Equals("@dtc", StringComparison.OrdinalIgnoreCase)) { isChange = true; result = DataTypeConversion(targetObJ, operand); } - else if (operation == "@data") + else if (operation.Equals("@call", StringComparison.OrdinalIgnoreCase)) { - isChange = true; - result = GetGlobleData(targetObJ, operand); + result = InvokeMethod(targetObJ, operand); } + //else if (operation.Equals("@set",StringComparison.OrdinalIgnoreCase)) + //{ + // isChange = true; + // result = SetMember(targetObJ, operand); + //} else { throw new NotSupportedException($"Operation {operation} is not supported."); @@ -151,59 +162,49 @@ namespace Serein.Library.Utils.SereinExpression private static object GetMember(object target, string memberPath) { if (target is null) return null; + + // 分割成员路径,按 '.' 处理多级访问 var members = memberPath.Split('.'); foreach (var member in members) { + var hasType = SereinExpressionExtension.TryGetType(member, out var memberName, out var type); + + // 检查成员是否包含数组索引,例如 "array[0]" "dict[key]" + var hasIndex = SereinExpressionExtension.TryGetIndex(member, out var elementName, out var strIndexKey); + // 检查成员是否包含数组索引,例如 "cars[0]" - var arrayIndexStart = member.IndexOf('['); - if (arrayIndexStart != -1) + if (hasIndex) { // 解析数组/集合名与索引部分 - var arrayName = member.Substring(0, arrayIndexStart); - var arrayIndexEnd = member.IndexOf(']'); - if (arrayIndexEnd == -1 || arrayIndexEnd <= arrayIndexStart + 1) - { - throw new ArgumentException($"Invalid array syntax for member {member}"); - } - var targetType = target?.GetType(); // 目标对象的类型 #region 处理键值对 if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>)) { - - var typetmp = target.GetType().FullName; // 目标是键值对 - var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1); var method = targetType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance); if (method != null) { - var result = method.Invoke(target, new object[] { indexStr }); - if (result != null) - { - target = result; - } + target = method.Invoke(target, new object[] { strIndexKey }); } - } #endregion else { - #region 表达式处理集合对象 // 获取数组或集合对象 // 如果arrayName为空,说明target可能是数组,而不需要再获取属性了 - if (!string.IsNullOrEmpty(arrayName)) + if (!string.IsNullOrEmpty(elementName)) { - var arrayProperty = target?.GetType().GetProperty(arrayName); + var arrayProperty = target?.GetType().GetProperty(elementName); if (arrayProperty is null) { - var arrayField = target?.GetType().GetField(arrayName); + var arrayField = target?.GetType().GetField(elementName); if (arrayField is null) { - throw new ArgumentException($"Member {arrayName} not found on target."); + throw new ArgumentException($"Member {elementName} not found on target."); } else { @@ -217,17 +218,16 @@ namespace Serein.Library.Utils.SereinExpression } // 提取数组索引 - var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1); - if (!int.TryParse(indexStr, out int index)) + if (!int.TryParse(strIndexKey, out int index)) { - throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}"); + throw new ArgumentException($"Invalid array index '{strIndexKey}' for member {member}"); } // 访问数组或集合中的指定索引 if (target is Array array) { if (index < 0 || index >= array.Length) { - throw new ArgumentException($"Index {index} out of bounds for array {arrayName}"); + throw new ArgumentException($"Index {index} out of bounds for array {elementName}"); } target = array.GetValue(index); } @@ -235,22 +235,22 @@ namespace Serein.Library.Utils.SereinExpression { if (index < 0 || index >= list.Count) { - throw new ArgumentException($"Index {index} out of bounds for list {arrayName}"); + throw new ArgumentException($"Index {index} out of bounds for list {elementName}"); } target = list[index]; } else { - throw new ArgumentException($"Member {arrayName} is not an array or list."); + throw new ArgumentException($"Member {elementName} is not an array or list."); } #endregion } - } else { // 处理非数组情况的属性或字段 - var property = target?.GetType().GetProperty(member); + + var property = target?.GetType().GetProperty(hasType ? memberName : member); if (property is null) { var field = target?.GetType().GetField(member); @@ -268,6 +268,13 @@ namespace Serein.Library.Utils.SereinExpression target = property.GetValue(target); } } + + if (hasType) + { + target = target.ToConvert(type); + } + + } return target; } @@ -404,6 +411,37 @@ namespace Serein.Library.Utils.SereinExpression return target; } + + + /// + /// 数据类型转换 + /// + /// + /// + /// + private static object DataTypeConversion(object value, string expression) + { + // 使用方法 + // @dtc value + // @dtc flowObj + if(!SereinExpressionExtension.TryGetType(expression,out var elementName, out var type)) + { + throw new ArgumentException($"无法获取类型:{expression}"); + } + + int endIndex = expression.IndexOf('>'); + if(endIndex == expression.Length -1) + { + return value.ToConvert(type); + } + else + { + string valueStr = expression.Substring(endIndex + 1, expression.Length - endIndex - 1); + return valueStr.ToValueData(type); + } + } + + /// /// 计算数学简单表达式 /// @@ -414,108 +452,22 @@ namespace Serein.Library.Utils.SereinExpression { return ComputedNumber(value, expression); } + + /// + /// 计算数学公式 + /// + /// + /// + /// + /// private static T ComputedNumber(object value, string expression) where T : struct, IComparable { T result = value.ToConvert(); return SerinArithmeticExpressionEvaluator.Evaluate(expression, result); } - /// - /// 数据类型转换 - /// - /// - /// - /// - private static object DataTypeConversion(object value, string expression) - { - Type tempType; - int typeStartIndex = expression.IndexOf('<'); - int typeEndIndex = expression.IndexOf('>'); - string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1) - .Trim().ToLower(); // 手动置顶的类型 - string valueStr = expression.Substring(typeEndIndex + 1, expression.Length - typeEndIndex - 1); - switch (typeStr) - { - case "bool": - tempType = typeof(bool); - break; - case "float": - tempType = typeof(float); - break; - case "decimal": - tempType = typeof(decimal); - break; - case "double": - tempType = typeof(double); - break; - case "sbyte": - tempType = typeof(sbyte); - break; - case "byte": - tempType = typeof(byte); - break; - case "short": - tempType = typeof(short); - break; - case "ushort": - tempType = typeof(ushort); - break; - case "int": - tempType = typeof(int); - break; - case "uint": - tempType = typeof(uint); - break; - case "long": - tempType = typeof(long); - break; - case "ulong": - tempType = typeof(ulong); - break; - // 如果需要支持 nint 和 nuint - // case "nint": - // tempType = typeof(nint); - // break; - // case "nuint": - // tempType = typeof(nuint); - // break; - case "string": - tempType = typeof(string); - break; - case "datetime": - if(valueStr.Equals("now", StringComparison.OrdinalIgnoreCase)) - { - return DateTime.Now; - } - tempType = typeof(DateTime); - break; - default: - tempType = Type.GetType(typeStr); - break; - } + - if (tempType.IsValueType) - { - return valueStr.ToValueData(tempType); - } - else - { - return null; - } - } - - - /// - /// 获取全局数据 - /// - /// - /// - /// - private static object GetGlobleData(object value, string expression) - { - var keyName = expression; - return SereinEnv.GetFlowGlobalData(keyName); - } } } diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index c64a6fd..25f6e91 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -387,6 +387,25 @@ namespace Serein.NodeFlow } await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 } + + nextNodes = singleFlipFlopNode.SuccessorNodes[ConnectionInvokeType.Upstream]; + for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--) + { + // 筛选出启用的节点 + if (!nextNodes[i].DebugSetting.IsEnable) + { + continue; + } + + context.SetPreviousNode(nextNodes[i], singleFlipFlopNode); + if (nextNodes[i].DebugSetting.IsInterrupt) // 执行触发前 + { + var cancelType = await nextNodes[i].DebugSetting.GetInterruptTask(); + await Console.Out.WriteLineAsync($"[{nextNodes[i].MethodDetails.MethodName}]中断已{cancelType},开始执行后继分支"); + } + await nextNodes[i].StartFlowAsync(context); // 启动执行触发器后继分支的节点 + } + context.Exit(); }); diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs index ffe081a..590ccad 100644 --- a/NodeFlow/Model/SingleConditionNode.cs +++ b/NodeFlow/Model/SingleConditionNode.cs @@ -146,10 +146,11 @@ namespace Serein.NodeFlow.Model } } + bool judgmentResult = false; try { - var isPass = SereinConditionParser.To(parameter, Expression); - context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail; + judgmentResult = SereinConditionParser.To(parameter, Expression); + context.NextOrientation = judgmentResult ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail; } catch (Exception ex) { @@ -158,7 +159,8 @@ namespace Serein.NodeFlow.Model } SereinEnv.WriteLine(InfoType.INFO, $"{result} {Expression} -> " + context.NextOrientation); - return result; + //return result; + return judgmentResult; } diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs index 44530c4..abe8f63 100644 --- a/WorkBench/App.xaml.cs +++ b/WorkBench/App.xaml.cs @@ -1,17 +1,23 @@ using Newtonsoft.Json; using Serein.Library; +using Serein.Library.Utils; +using Serein.Library.Utils.SereinExpression; using System.Diagnostics; using System.IO; using System.Linq.Expressions; using System.Windows; +using System.Windows.Media.Animation; using System.Windows.Threading; namespace Serein.Workbench { - //public class A - //{ - // public object Data { get; set; } - //} +#if DEBUG + public class A + { + public string Data { get; set; } = "1234"; + public bool Data2 { get; set; } + } +#endif @@ -26,10 +32,31 @@ namespace Serein.Workbench #if DEBUG if (1 == 1) { - //var A = new A(); - //A.Data = true; - //var expression = ".Data == True"; - //var pass = Serein.Library.Utils.SereinExpression.SereinConditionParser.To(A, expression); + //object Data = "false"; + //var expression = "== false"; + //var pass = Serein.Library.Utils.SereinExpression.SereinConditionParser.To(Data, expression); + + + //string[] objects = new string[] + //{ + // "124", + // "true", + // "0.42" + //}; + //Dictionary keyValuePairs = new Dictionary + //{ + // {"value", objects } + //}; + + + //var data = SerinExpressionEvaluator.Evaluate("@Get .[value].[0]", keyValuePairs, out _); + //data = SerinExpressionEvaluator.Evaluate("@Get .[value].[1]", keyValuePairs, out _); + //data = SerinExpressionEvaluator.Evaluate("@Dtc ", data, out _); + //var result = SereinConditionParser.To(data, "== True"); + + + //SereinEnv.AddOrUpdateFlowGlobalData("My", A); + //var data = SerinExpressionEvaluator.Evaluate("@Get #My#",null,out _); // 这里是我自己的测试代码,你可以删除 diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index abfdb48..5ed8847 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -593,7 +593,8 @@ namespace Serein.Workbench eventArgs.ArgIndex, eventArgs.ConnectionArgSourceType, startJunction, - endJunction + endJunction, + IToJunction ); Connections.Add(connection); fromNodeControl.AddCnnection(connection); diff --git a/Workbench/Node/INodeJunction.cs b/Workbench/Node/INodeJunction.cs index e22d61f..e532f72 100644 --- a/Workbench/Node/INodeJunction.cs +++ b/Workbench/Node/INodeJunction.cs @@ -30,5 +30,15 @@ namespace Serein.Workbench.Node /// 返回值控制点 /// JunctionControlBase ReturnDataJunction { get; } + + /// + /// 获取目标参数控制点,用于防止wpf释放资源导致找不到目标节点,返回-1,-1的坐标 + /// + /// + /// + JunctionControlBase GetJunctionOfArgData(int index) + { + return ArgDataJunction[index]; + } } } diff --git a/Workbench/Node/Junction/ConnectionLineShape.cs b/Workbench/Node/Junction/ConnectionLineShape.cs index 5054706..122adaf 100644 --- a/Workbench/Node/Junction/ConnectionLineShape.cs +++ b/Workbench/Node/Junction/ConnectionLineShape.cs @@ -62,6 +62,8 @@ namespace Serein.Workbench.Node.View InitElementPoint(isDotted, isTop); InvalidateVisual(); // 触发重绘 } + + public void InitElementPoint(bool isDotted , bool isTop = false) { hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线 diff --git a/Workbench/Node/Junction/JunctionControlBase.cs b/Workbench/Node/Junction/JunctionControlBase.cs index 5ce870c..fbcabea 100644 --- a/Workbench/Node/Junction/JunctionControlBase.cs +++ b/Workbench/Node/Junction/JunctionControlBase.cs @@ -31,10 +31,10 @@ namespace Serein.Workbench.Node.View } } + public class ParamsArgControl: Shape { - public ParamsArgControl() { this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除 diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs index ba32140..907772e 100644 --- a/Workbench/Node/View/ConnectionControl.cs +++ b/Workbench/Node/View/ConnectionControl.cs @@ -113,6 +113,11 @@ namespace Serein.Workbench.Node.View /// public ConnectionInvokeType InvokeType { get; } + /// + /// 目标节点控制点 + /// + private INodeJunction EndNode; + /// /// 获取参数类型,第几个参数 /// @@ -138,6 +143,8 @@ namespace Serein.Workbench.Node.View /// private ConnectionLineShape BezierLine; + + private LineType LineType; /// @@ -168,7 +175,8 @@ namespace Serein.Workbench.Node.View int argIndex, ConnectionArgSourceType argSourceType, JunctionControlBase Start, - JunctionControlBase End) + JunctionControlBase End, + INodeJunction nodeJunction) { this.LineType = LineType; this.Canvas = Canvas; @@ -176,6 +184,7 @@ namespace Serein.Workbench.Node.View this.ArgSourceType = argSourceType; this.Start = Start; this.End = End; + this.EndNode = nodeJunction; InitElementPoint(); } @@ -242,6 +251,10 @@ namespace Serein.Workbench.Node.View /// public void RefreshLine() { + if(ArgIndex > -1) + { + End = EndNode.GetJunctionOfArgData(ArgIndex); + } (Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End); BezierLine.UpdatePoints(startPoint, endPoint); } @@ -255,7 +268,6 @@ namespace Serein.Workbench.Node.View private (Point startPoint, Point endPoint) RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement) { - var startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置 var endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置 return (startPoint, endPoint);