From 51bdbab4d126229700e7ec23ab2733248f9fcf7a Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Fri, 27 Sep 2024 23:47:25 +0800
Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E4=BA=86=E8=8A=82=E7=82=B9?=
=?UTF-8?q?=E6=A0=91=E9=A2=84=E8=A7=88=E3=80=81=E8=8A=82=E7=82=B9=E5=AE=9A?=
=?UTF-8?q?=E4=BD=8D=EF=BC=8C=E5=AE=B9=E5=99=A8=E5=AF=B9=E8=B1=A1=E9=A2=84?=
=?UTF-8?q?=E8=A7=88?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Library/Api/IFlowEnvironment.cs | 77 ++---
Net461DllTest/Flow/LogicControl.cs | 6 +-
Net461DllTest/Flow/ViewLogicControl.cs | 12 +-
Net461DllTest/Signal/OrderSignal.cs | 11 +-
NodeFlow/FlowEnvironment.cs | 15 +-
NodeFlow/FlowStarter.cs | 8 +-
NodeFlow/Tool/ExpressionHelper.cs | 1 +
NodeFlow/Tool/ObjDynamicCreateHelper.cs | 308 ++++++++++++++++++
.../SereinExpression/SereinConditionParser.cs | 20 +-
.../SerinExpressionEvaluator.cs | 181 ++++++++--
WorkBench/App.xaml.cs | 4 +
WorkBench/LogWindow.xaml | 2 +-
WorkBench/MainWindow.xaml | 48 ++-
WorkBench/MainWindow.xaml.cs | 231 +++++++++++--
WorkBench/Node/NodeControlViewModelBase.cs | 2 +-
WorkBench/Themes/NodeTreeItemViewControl.xaml | 17 +-
.../Themes/NodeTreeItemViewControl.xaml.cs | 31 +-
17 files changed, 793 insertions(+), 181 deletions(-)
create mode 100644 NodeFlow/Tool/ObjDynamicCreateHelper.cs
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index 8662879..f02f3be 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -345,32 +345,23 @@ namespace Serein.Library.Api
}
public string Key { get; private set; }
public object Instance { get; private set; }
-
}
- //public class IOCMembersChangedEventArgs : FlowEventArgs
- //{
- // //public enum EventType
- // //{
- // // ///
- // // /// 登记了类型
- // // ///
- // // Registered,
- // // ///
- // // /// 构建了类型
- // // ///
- // // Completeuild,
- // //}
- // public IOCMembersChangedEventArgs(Type[] types, object[] dependencies, object[] unfinishedDependencies)
- // {
- // this.Types = types;
- // this.Dependencies = dependencies;
- // this.UnfinishedDependencies = unfinishedDependencies;
- // }
- // public Type[] Types { get; protected set; }
- // public object[] Dependencies { get; private set; }
- // public object[] UnfinishedDependencies { get; private set; }
- //}
+ ///
+ /// 节点需要定位
+ ///
+ ///
+ public delegate void NodeLocatedHandler(NodeLocatedEventArgs eventArgs);
+
+ public class NodeLocatedEventArgs : FlowEventArgs
+ {
+ public NodeLocatedEventArgs(string nodeGuid)
+ {
+ NodeGuid = nodeGuid;
+ }
+ public string NodeGuid { get; private set; }
+ }
+
public interface IFlowEnvironment
{
#region 属性
@@ -447,6 +438,11 @@ namespace Serein.Library.Api
event IOCMembersChangedHandler OnIOCMembersChanged;
+ ///
+ /// 节点需要定位
+ ///
+ event NodeLocatedHandler OnNodeLocate;
+
#endregion
@@ -461,7 +457,7 @@ namespace Serein.Library.Api
//bool TryGetNodeData(string methodName, out NodeData node);
- #region Workbench
+ #region 环境基础接口
///
/// 保存当前项目
@@ -501,8 +497,6 @@ namespace Serein.Library.Api
///
void Exit();
-
-
///
/// 设置流程起点节点
///
@@ -549,21 +543,7 @@ namespace Serein.Library.Api
///
///
bool AddInterruptExpression(string key, string expression);
- ///
- /// 添加作用于指定节点的中断表达式
- ///
- ///
- ///
- ///
- // bool AddInterruptExpression(string nodeGuid,string expression);
-
- //
- // 设置节点数据监视状态
- //
- // 需要监视的节点Guid
- // 是否监视
- // void SetNodeFLowDataMonitorState(string nodeGuid, bool isMonitor);
-
+
///
/// 监视指定对象
///
@@ -591,7 +571,7 @@ namespace Serein.Library.Api
#endregion
- #region Start
+ #region 启动器调用
///
/// 流程启动器调用,监视数据更新通知
@@ -609,6 +589,17 @@ namespace Serein.Library.Api
void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type);
+ #endregion
+
+
+ #region UI视觉
+
+ ///
+ /// 节点定位
+ ///
+ ///
+ void NodeLocated(string nodeGuid);
+
#endregion
}
}
diff --git a/Net461DllTest/Flow/LogicControl.cs b/Net461DllTest/Flow/LogicControl.cs
index 0562ac4..58a46b2 100644
--- a/Net461DllTest/Flow/LogicControl.cs
+++ b/Net461DllTest/Flow/LogicControl.cs
@@ -17,10 +17,10 @@ using System.Threading.Tasks;
namespace Net461DllTest.Flow
{
- [DynamicFlow] // 标记该类存在节点方法
+ [DynamicFlow]
public class LogicControl
{
- [AutoInjection] // 标记该属性为依赖项,需要注入
+ [AutoInjection]
public PlcDevice MyPlc { get; set; }
@@ -58,7 +58,7 @@ namespace Net461DllTest.Flow
#region 触发器
[NodeAction(NodeType.Flipflop, "等待信号触发", ReturnType = typeof(int))]
- public async Task WaitTask(OrderSignal order = OrderSignal.A)
+ public async Task WaitTask(OrderSignal order = OrderSignal.Command_1)
{
try
{
diff --git a/Net461DllTest/Flow/ViewLogicControl.cs b/Net461DllTest/Flow/ViewLogicControl.cs
index ef15caa..3bc4435 100644
--- a/Net461DllTest/Flow/ViewLogicControl.cs
+++ b/Net461DllTest/Flow/ViewLogicControl.cs
@@ -19,12 +19,13 @@ namespace Net461DllTest.Flow
{
private List
public event IOCMembersChangedHandler OnIOCMembersChanged;
+ ///
+ /// 节点需要定位
+ ///
+ public event NodeLocatedHandler OnNodeLocate;
#endregion
#region 属性
@@ -1037,7 +1041,16 @@ namespace Serein.NodeFlow
#endregion
- #region 网络交互
+ #region 视觉效果
+
+ ///
+ /// 定位节点
+ ///
+ ///
+ public void NodeLocated(string nodeGuid)
+ {
+ OnNodeLocate?.Invoke(new NodeLocatedEventArgs(nodeGuid));
+ }
#endregion
diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs
index 1b58a00..e3c3ed9 100644
--- a/NodeFlow/FlowStarter.cs
+++ b/NodeFlow/FlowStarter.cs
@@ -150,10 +150,10 @@ namespace Serein.NodeFlow
#region 初始化运行环境的Ioc容器
// 清除节点使用的对象,筛选出需要初始化的方法描述
var thisRuningMds = new List();
- thisRuningMds.AddRange(runNodeMd.Where(md => md is not null));
- thisRuningMds.AddRange(initMethods.Where(md => md is not null));
- thisRuningMds.AddRange(loadingMethods.Where(md => md is not null));
- thisRuningMds.AddRange(exitMethods.Where(md => md is not null));
+ thisRuningMds.AddRange(runNodeMd.Where(md => md?.ActingInstanceType is not null));
+ thisRuningMds.AddRange(initMethods.Where(md => md?.ActingInstanceType is not null));
+ thisRuningMds.AddRange(loadingMethods.Where(md => md?.ActingInstanceType is not null));
+ thisRuningMds.AddRange(exitMethods.Where(md => md?.ActingInstanceType is not null));
// .AddRange(initMethods).AddRange(loadingMethods).a
foreach (var nodeMd in thisRuningMds)
diff --git a/NodeFlow/Tool/ExpressionHelper.cs b/NodeFlow/Tool/ExpressionHelper.cs
index 5e75af5..832d86b 100644
--- a/NodeFlow/Tool/ExpressionHelper.cs
+++ b/NodeFlow/Tool/ExpressionHelper.cs
@@ -673,6 +673,7 @@ namespace Serein.NodeFlow.Tool
*/
#endregion
+
#region 暂时不删(已注释)
/* ///
/// 表达式树构建多个参数,有返回值的方法
diff --git a/NodeFlow/Tool/ObjDynamicCreateHelper.cs b/NodeFlow/Tool/ObjDynamicCreateHelper.cs
new file mode 100644
index 0000000..a30fcf2
--- /dev/null
+++ b/NodeFlow/Tool/ObjDynamicCreateHelper.cs
@@ -0,0 +1,308 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection.Emit;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System;
+using System.Collections.Generic;
+using System.Reflection;
+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);
+ //SetPropertyValues(obj, properties);
+ return obj;
+ }
+ public static bool TryResolve(Dictionary 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();
+ foreach (var prop in objType.GetProperties())
+ {
+ var value = prop.GetValue(obj);
+ Console.WriteLine($"{indent}{prop.Name} (Type: {prop.PropertyType.Name}): {value}");
+
+ if (value != null)
+ {
+ if (prop.PropertyType.IsArray) // 处理数组类型
+ {
+ var array = (Array)value;
+ Console.WriteLine($"{indent}{prop.Name} is an array with {array.Length} elements:");
+ for (int i = 0; i < array.Length; i++)
+ {
+ var element = array.GetValue(i);
+ if (element != null && element.GetType().IsClass && !(element is string))
+ {
+ Console.WriteLine($"{indent}\tArray[{i}] (Type: {element.GetType().Name}) contains a nested object:");
+ PrintObjectProperties(element, indent + "\t\t");
+ }
+ else
+ {
+ Console.WriteLine($"{indent}\tArray[{i}] (Type: {element?.GetType().Name}): {element}");
+ }
+ }
+ }
+ else if (value.GetType().IsClass && !(value is string)) // 处理嵌套对象
+ {
+ Console.WriteLine($"{indent}{prop.Name} contains a nested object:");
+ PrintObjectProperties(value, indent + "\t");
+ }
+ }
+ }
+ }
+
+
+
+ // 方法 1: 创建动态类型及其对象实例
+ public static object CreateObjectWithProperties(Dictionary properties, string typeName)
+ {
+ // 如果类型已经缓存,直接返回缓存的类型
+ if (typeCache.ContainsKey(typeName))
+ {
+ return Activator.CreateInstance(typeCache[typeName]);
+ }
+
+ // 定义动态程序集和模块
+ var assemblyName = new AssemblyName("DynamicAssembly");
+ var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
+ var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
+
+ // 定义动态类型
+ var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
+
+ // 为每个属性名和值添加相应的属性到动态类型中
+ foreach (var kvp in properties)
+ {
+ string propName = kvp.Key;
+ object propValue = kvp.Value;
+ Type propType;
+
+ if (propValue is IList>) // 处理数组类型
+ {
+ var nestedPropValue = (propValue as IList>)[0];
+ var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
+ propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
+ }
+ else if(propValue is Dictionary nestedProperties)
+ {
+ // 如果值是嵌套的字典,递归创建嵌套类型
+ propType = CreateObjectWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
+ }
+ else
+ {
+ // 如果是普通类型,使用值的类型
+ propType = propValue?.GetType() ?? typeof(object);
+ }
+
+ // 定义私有字段和公共属性
+ var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
+ var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
+
+ // 定义 getter 方法
+ var getMethodBuilder = typeBuilder.DefineMethod(
+ "get_" + propName,
+ MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
+ propType,
+ Type.EmptyTypes);
+
+ var getIL = getMethodBuilder.GetILGenerator();
+ getIL.Emit(OpCodes.Ldarg_0);
+ getIL.Emit(OpCodes.Ldfld, fieldBuilder);
+ getIL.Emit(OpCodes.Ret);
+
+ // 定义 setter 方法
+ var setMethodBuilder = typeBuilder.DefineMethod(
+ "set_" + propName,
+ MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
+ null,
+ new Type[] { propType });
+
+ var setIL = setMethodBuilder.GetILGenerator();
+ setIL.Emit(OpCodes.Ldarg_0);
+ setIL.Emit(OpCodes.Ldarg_1);
+ setIL.Emit(OpCodes.Stfld, fieldBuilder);
+ setIL.Emit(OpCodes.Ret);
+
+ // 将 getter 和 setter 方法添加到属性
+ propertyBuilder.SetGetMethod(getMethodBuilder);
+ propertyBuilder.SetSetMethod(setMethodBuilder);
+ }
+
+ // 创建类型并缓存
+ var dynamicType = typeBuilder.CreateType();
+ typeCache[typeName] = dynamicType;
+
+ // 创建对象实例
+ return Activator.CreateInstance(dynamicType);
+ }
+
+ // 方法 2: 递归设置对象的属性值
+ public static void SetPropertyValues(object obj, Dictionary properties)
+ {
+ var objType = obj.GetType();
+
+ foreach (var kvp in properties)
+ {
+ var propInfo = objType.GetProperty(kvp.Key);
+ object value = kvp.Value;
+
+ // 如果值是嵌套的字典类型,递归处理嵌套对象
+ if (value is Dictionary nestedProperties)
+ {
+ // 创建嵌套对象
+ var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
+
+ // 递归设置嵌套对象的值
+ SetPropertyValues(nestedObj, nestedProperties);
+
+ // 将嵌套对象赋值给属性
+ propInfo.SetValue(obj, nestedObj);
+ }
+ else
+ {
+ // 直接赋值给属性
+ propInfo.SetValue(obj, value);
+ }
+ }
+ }
+ // 方法 2: 递归设置对象的属性值(带验证)
+
+ public static bool SetPropertyValuesWithValidation(object obj, Dictionary properties)
+ {
+ var objType = obj.GetType();
+ bool allSuccessful = true; // 标记是否所有属性赋值成功
+
+ foreach (var kvp in properties)
+ {
+ var propName = kvp.Key;
+ var propValue = kvp.Value;
+
+ var propInfo = objType.GetProperty(propName);
+
+ if (propInfo == null)
+ {
+ // 属性不存在,打印警告并标记失败
+ Console.WriteLine($"Warning: 属性 '{propName}' 不存在于类型 '{objType.Name}' 中,跳过赋值。");
+ allSuccessful = false;
+ continue;
+ }
+
+ // 检查属性类型是否与要赋的值兼容
+ var targetType = propInfo.PropertyType;
+ if (!IsCompatibleType(targetType, propValue))
+ {
+ // 如果类型不兼容,打印错误并标记失败
+ Console.WriteLine($"Error: 无法将类型 '{propValue?.GetType().Name}' 赋值给属性 '{propName}' (Type: {targetType.Name}),跳过赋值。");
+ allSuccessful = false;
+ continue;
+ }
+
+ try
+ {
+ // 如果值是一个嵌套对象,递归赋值
+ if (propValue is Dictionary nestedProperties)
+ {
+ var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
+ if (SetPropertyValuesWithValidation(nestedObj, nestedProperties))
+ {
+ propInfo.SetValue(obj, nestedObj);
+ }
+ else
+ {
+ allSuccessful = false; // 嵌套赋值失败
+ }
+ }
+ else if (propValue is IList> list) // 处理列表
+ {
+ // 获取目标类型的数组元素类型
+ var elementType = propInfo.PropertyType.GetElementType();
+ var array = Array.CreateInstance(elementType, list.Count);
+
+ for (int i = 0; i < list.Count; i++)
+ {
+ var item = Activator.CreateInstance(elementType);
+ if (SetPropertyValuesWithValidation(item, list[i]))
+ {
+ array.SetValue(item, i);
+ }
+ else
+ {
+ allSuccessful = false; // 赋值失败
+ }
+ }
+
+ propInfo.SetValue(obj, array);
+ }
+ else
+ {
+ // 直接赋值
+ propInfo.SetValue(obj, propValue);
+ }
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Error: 为属性 '{propName}' 赋值时发生异常:{ex.Message}");
+ allSuccessful = false;
+ }
+ }
+
+ return allSuccessful;
+ }
+
+ // 检查类型兼容性的方法(支持嵌套类型)
+ static bool IsCompatibleType(Type targetType, object value)
+ {
+ if (value == null)
+ {
+ // 如果值为null,且目标类型是引用类型或者可空类型,则兼容
+ return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
+ }
+
+ // 检查值的类型是否与目标类型相同,或是否可以转换为目标类型
+ if (targetType.IsAssignableFrom(value.GetType()))
+ {
+ return true;
+ }
+
+ // 处理数组类型
+ if (targetType.IsArray)
+ {
+ // 检查数组的元素类型与值的类型兼容
+ var elementType = targetType.GetElementType();
+ return value is IList>; // 假设值是一个列表,具体处理逻辑在赋值时
+ }
+
+ // 处理嵌套类型的情况
+ if (value is Dictionary && targetType.IsClass && !targetType.IsPrimitive)
+ {
+ // 如果目标类型是一个复杂对象,并且值是一个字典,可能是嵌套对象
+ return true; // 假设可以递归处理嵌套对象
+ }
+
+ return false;
+ }
+
+
+
+
+ }
+}
\ No newline at end of file
diff --git a/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs b/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs
index 4b486ab..f5008e4 100644
--- a/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs
+++ b/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs
@@ -13,7 +13,10 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
try
{
-
+ if (string.IsNullOrEmpty(expression))
+ {
+ return false;
+ }
var parse = ConditionParse(data, expression);
var result = parse.Evaluate(data);
return result;
@@ -104,7 +107,8 @@ namespace Serein.NodeFlow.Tool.SereinExpression
{
// 如果不需要转为指定类型
memberPath = operatorStr;
- targetObj = GetMemberValue(data, operatorStr);
+ targetObj = SerinExpressionEvaluator.Evaluate("@get " + operatorStr, data, out _);
+ //targetObj = GetMemberValue(data, operatorStr);
type = targetObj.GetType();
operatorStr = parts[1].ToLower(); //
valueStr = string.Join(' ', parts.Skip(2));
@@ -189,18 +193,6 @@ namespace Serein.NodeFlow.Tool.SereinExpression
}
-
-
- //int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
-
- //return new MemberConditionResolver
- //{
- // TargetObj = targetObj,
- // //MemberPath = memberPath,
- // Op = ParseValueTypeOperator(operatorStr),
- // Value = value,
- // ArithmeticExpression = GetArithmeticExpression(parts[0])
- //};
}
#endregion
#region 解析类型 double
diff --git a/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs b/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs
index 6cf1d02..9125247 100644
--- a/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs
+++ b/NodeFlow/Tool/SereinExpression/SerinExpressionEvaluator.cs
@@ -121,40 +121,98 @@ namespace Serein.NodeFlow.Tool.SereinExpression
///
private static object GetMember(object target, string memberPath)
{
+ // 分割成员路径,按 '.' 处理多级访问
var members = memberPath.Split('.');
+
foreach (var member in members)
{
+ if (target == null) return null;
- if (target is null) return null;
-
-
- var property = target.GetType().GetProperty(member);
- if (property != null)
+ // 检查成员是否包含数组索引,例如 "cars[0]"
+ var arrayIndexStart = member.IndexOf('[');
+ if (arrayIndexStart != -1)
{
-
- target = property.GetValue(target);
-
- }
- else
- {
- var field = target.GetType().GetField(member);
- if (field != null)
+ // 解析数组/集合名与索引部分
+ 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}");
+ }
- target = field.GetValue(target);
+ // 提取数组索引
+ var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
+ if (!int.TryParse(indexStr, out int index))
+ {
+ throw new ArgumentException($"Invalid array index '{indexStr}' for member {member}");
+ }
+ // 获取数组或集合对象
+ var arrayProperty = target.GetType().GetProperty(arrayName);
+ if (arrayProperty != null)
+ {
+ target = arrayProperty.GetValue(target);
}
else
{
- throw new ArgumentException($"Member {member} not found on target.");
+ var arrayField = target.GetType().GetField(arrayName);
+ if (arrayField != null)
+ {
+ target = arrayField.GetValue(target);
+ }
+ else
+ {
+ throw new ArgumentException($"Member {arrayName} not found on target.");
+ }
+ }
+
+ // 访问数组或集合中的指定索引
+ if (target is Array array)
+ {
+ if (index < 0 || index >= array.Length)
+ {
+ throw new ArgumentException($"Index {index} out of bounds for array {arrayName}");
+ }
+ target = array.GetValue(index);
+ }
+ else if (target is IList