using System;
|
using System.Collections.Generic;
|
using System.Linq;
|
using System.Text;
|
using System.Threading.Tasks;
|
using System.Linq.Expressions;
|
using System.Reflection;
|
using LB_VisionProcesses;
|
using System.Collections.Concurrent;
|
using System.Diagnostics;
|
|
namespace LB_SmartVision.ProcessRun
|
{
|
public class ExpressionTreeMappingEngine
|
{
|
private ConcurrentDictionary<string, List<Action<IProcess>>> _compiledMappings;
|
private readonly ConcurrentDictionary<string, IProcess> _processContext;
|
|
public ExpressionTreeMappingEngine(ConcurrentDictionary<string, IProcess> processContext)
|
{
|
_processContext = processContext;
|
_compiledMappings = new ConcurrentDictionary<string, List<Action<IProcess>>>();
|
}
|
|
public void CompileMappings(ConcurrentDictionary<string, List<Tuple<string, string>>> dicInputsMapping)
|
{
|
foreach (var processMapping in dicInputsMapping)
|
{
|
var processName = processMapping.Key;
|
var actions = new List<Action<IProcess>>();
|
|
foreach (var tuple in processMapping.Value)
|
{
|
if (string.IsNullOrEmpty(tuple.Item2?.Trim()))
|
continue;
|
|
var action = CreateMappingAction(tuple.Item1, tuple.Item2);
|
if (action != null)
|
actions.Add(action);
|
}
|
|
_compiledMappings[processName] = actions;
|
}
|
}
|
|
private Action<IProcess> CreateMappingAction(string inputPath, string outputPath)
|
{
|
try
|
{
|
// 解析输入路径:TargetProcess.InputType.InputProperty
|
var inputParts = inputPath.Split('.');
|
if (inputParts.Length < 3) return null;
|
|
string targetProcessName = inputParts[0];
|
string inputType = inputParts[1]; // 通常是 "Inputs"
|
string inputProperty = inputParts[2];
|
|
// 解析输出路径:SourceProcess.OutputType.OutputProperty
|
var outputParts = outputPath.Split('.');
|
if (outputParts.Length < 2) return null;
|
|
string sourceProcessName = outputParts[0];
|
string outputType = outputParts[1]; // "Inputs" 或 "Outputs"
|
string outputProperty = outputParts.Length == 3 ? outputParts[2] : "";
|
|
// 创建表达式参数
|
var targetParameter = Expression.Parameter(typeof(IProcess), "targetProcess");
|
|
// 构建获取源值的表达式
|
var sourceValueExpression = BuildSourceValueExpression(sourceProcessName, outputType, outputProperty);
|
if (sourceValueExpression == null) return null;
|
|
// 构建设置目标值的表达式
|
var setTargetExpression = BuildSetTargetExpression(targetParameter, inputType, inputProperty, sourceValueExpression);
|
if (setTargetExpression == null) return null;
|
|
// 编译为委托
|
var lambda = Expression.Lambda<Action<IProcess>>(setTargetExpression, targetParameter);
|
return lambda.Compile();
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"创建映射动作失败: {ex.Message}");
|
return null;
|
}
|
}
|
|
private Expression BuildSourceValueExpression(string sourceProcessName, string outputType, string outputProperty)
|
{
|
// 获取源流程实例:_processContext[sourceProcessName]
|
var contextField = Expression.Constant(_processContext);
|
var processNameConstant = Expression.Constant(sourceProcessName);
|
var getItemMethod = typeof(ConcurrentDictionary<string, IProcess>).GetMethod("get_Item");
|
var getProcessExpr = Expression.Call(contextField, getItemMethod, processNameConstant);
|
|
// 转换为 IProcess
|
var convertExpr = Expression.Convert(getProcessExpr, typeof(IProcess));
|
|
// 添加 null 检查
|
var nullCheck = Expression.NotEqual(convertExpr, Expression.Constant(null, typeof(IProcess)));
|
|
// 根据输出类型和属性构建获取值的表达式
|
Expression valueExpression = outputType switch
|
{
|
"Inputs" => BuildInputsValueExpression(convertExpr, outputProperty),
|
"Outputs" => BuildOutputsValueExpression(convertExpr, outputProperty),
|
_ => Expression.Constant(null, typeof(object))
|
};
|
|
// 如果源流程为 null,返回 null,否则返回值
|
return Expression.Condition(nullCheck, Expression.Convert(valueExpression, typeof(object)), Expression.Constant(null, typeof(object)));
|
}
|
|
private Expression BuildInputsValueExpression(Expression processExpr, string propertyName)
|
{
|
if (propertyName == "Image")
|
{
|
// 首先尝试字段,然后尝试属性
|
return GetMemberExpression(processExpr, "InputImage");
|
}
|
else
|
{
|
return BuildInputsConcurrentDictionaryExpression(processExpr, propertyName);
|
}
|
}
|
|
private Expression BuildOutputsValueExpression(Expression processExpr, string propertyName)
|
{
|
if (propertyName == "Image")
|
{
|
// 首先尝试字段,然后尝试属性
|
return GetMemberExpression(processExpr, "OutputImage");
|
}
|
else if (propertyName == "Result")
|
{
|
// 首先尝试字段,然后尝试属性
|
return GetMemberExpression(processExpr, "Result");
|
}
|
else if (propertyName == "Fixture")
|
{
|
// 通过 Params 获取 Fixture
|
var paramsExpr = GetMemberExpression(processExpr, "Params");
|
return GetMemberExpression(paramsExpr, "Fixture");
|
}
|
else
|
{
|
return BuildOutputsConcurrentDictionaryExpression(processExpr, propertyName);
|
}
|
}
|
|
private Expression BuildInputsConcurrentDictionaryExpression(Expression processExpr, string propertyName)
|
{
|
// process.Params.Inputs[propertyName]
|
var paramsExpr = GetMemberExpression(processExpr, "Params");
|
var inputsExpr = GetMemberExpression(paramsExpr, "Inputs");
|
|
var indexer = inputsExpr.Type.GetProperty("Item", new[] { typeof(string) });
|
if (indexer == null)
|
throw new InvalidOperationException("Inputs 类型没有字符串索引器");
|
|
var propertyNameConstant = Expression.Constant(propertyName);
|
return Expression.Property(inputsExpr, indexer, propertyNameConstant);
|
}
|
|
private Expression BuildOutputsConcurrentDictionaryExpression(Expression processExpr, string propertyName)
|
{
|
// process.Params.Outputs[propertyName]
|
var paramsExpr = GetMemberExpression(processExpr, "Params");
|
var outputsExpr = GetMemberExpression(paramsExpr, "Outputs");
|
|
var indexer = outputsExpr.Type.GetProperty("Item", new[] { typeof(string) });
|
if (indexer == null)
|
throw new InvalidOperationException("Outputs 类型没有字符串索引器");
|
|
var propertyNameConstant = Expression.Constant(propertyName);
|
return Expression.Property(outputsExpr, indexer, propertyNameConstant);
|
}
|
|
/// <summary>
|
/// 通用的成员访问方法:首先尝试字段,然后尝试属性
|
/// </summary>
|
private Expression GetMemberExpression(Expression instance, string memberName)
|
{
|
var type = instance.Type;
|
|
// 首先尝试字段
|
var field = type.GetField(memberName, BindingFlags.Public | BindingFlags.Instance);
|
if (field != null)
|
{
|
return Expression.Field(instance, field);
|
}
|
|
// 然后尝试属性
|
var property = type.GetProperty(memberName, BindingFlags.Public | BindingFlags.Instance);
|
if (property != null)
|
{
|
return Expression.Property(instance, property);
|
}
|
|
throw new InvalidOperationException($"在类型 {type.Name} 中找不到字段或属性: {memberName}");
|
}
|
|
private Expression BuildSetTargetExpression(ParameterExpression targetParameter, string inputType, string inputProperty, Expression sourceValueExpression)
|
{
|
return inputProperty switch
|
{
|
"Image" => BuildSetImageExpression(targetParameter, sourceValueExpression),
|
"Fixture" => BuildSetFixtureExpression(targetParameter, sourceValueExpression),
|
"Result" => BuildSetResultExpression(targetParameter, inputProperty, sourceValueExpression),
|
_ => BuildSetInputExpression(targetParameter, inputProperty, sourceValueExpression)
|
};
|
}
|
|
private Expression BuildSetImageExpression(ParameterExpression target, Expression sourceValue)
|
{
|
// target.InputImage = sourceValue
|
var inputImageExpr = GetMemberExpression(target, "InputImage");
|
return Expression.Assign(inputImageExpr, Expression.Convert(sourceValue, inputImageExpr.Type));
|
}
|
|
private Expression BuildSetFixtureExpression(ParameterExpression target, Expression sourceValue)
|
{
|
//// target.Params.Fixture = sourceValue ?? new Fixture()
|
//var paramsExpr = GetMemberExpression(target, "Params");
|
//var fixtureExpr = GetMemberExpression(paramsExpr, "Fixture");
|
|
//// 如果源值为null,创建新的Fixture
|
//var newFixtureExpr = Expression.New(typeof(Fixture));
|
//var coalesceExpr = Expression.Coalesce(
|
// Expression.Convert(sourceValue, typeof(Fixture)),
|
// newFixtureExpr
|
//);
|
|
//return Expression.Assign(fixtureExpr, coalesceExpr);
|
|
// 如果源值为null,就设置为null,不创建新对象
|
var paramsExpr = GetMemberExpression(target, "Params");
|
var fixtureExpr = GetMemberExpression(paramsExpr, "Fixture");
|
|
var convertedSourceValue = Expression.Convert(sourceValue, typeof(Fixture));
|
return Expression.Assign(fixtureExpr, convertedSourceValue);
|
}
|
|
private Expression BuildSetResultExpression(ParameterExpression target, string propertyName, Expression sourceValue)
|
{
|
// target.Params.Inputs[propertyName] = sourceValue
|
var paramsExpr = GetMemberExpression(target, "Params");
|
var inputsExpr = GetMemberExpression(paramsExpr, "Inputs");
|
|
var indexer = inputsExpr.Type.GetProperty("Item", new[] { typeof(string) });
|
if (indexer == null)
|
return Expression.Empty();
|
|
var propertyNameConstant = Expression.Constant(propertyName);
|
var targetIndexExpr = Expression.Property(inputsExpr, indexer, propertyNameConstant);
|
|
return Expression.Assign(targetIndexExpr, Expression.Convert(sourceValue, targetIndexExpr.Type));
|
}
|
|
private Expression BuildSetInputExpression(ParameterExpression target, string propertyName, Expression sourceValue)
|
{
|
// 检查字典是否包含该键,如果包含则赋值
|
var paramsExpr = GetMemberExpression(target, "Params");
|
var inputsExpr = GetMemberExpression(paramsExpr, "Inputs");
|
|
var containsMethod = inputsExpr.Type.GetMethod("Contains", new[] { typeof(string) });
|
if (containsMethod == null)
|
return Expression.Empty();
|
|
var propertyNameConstant = Expression.Constant(propertyName);
|
var containsCall = Expression.Call(inputsExpr, containsMethod, propertyNameConstant);
|
|
// 如果包含键,则赋值
|
var indexer = inputsExpr.Type.GetProperty("Item", new[] { typeof(string) });
|
if (indexer == null)
|
return Expression.Empty();
|
|
var targetIndexExpr = Expression.Property(inputsExpr, indexer, propertyNameConstant);
|
var assignExpr = Expression.Assign(
|
targetIndexExpr,
|
Expression.Convert(sourceValue, targetIndexExpr.Type)
|
);
|
|
// 条件表达式:if (inputs.Contains(propertyName)) { inputs[propertyName] = sourceValue; }
|
return Expression.IfThen(containsCall, assignExpr);
|
}
|
|
public void UpdateInputs(IProcess process)
|
{
|
if (_compiledMappings.TryGetValue(process.strProcessName, out var actions))
|
{
|
foreach (var action in actions)
|
{
|
try
|
{
|
action(process);
|
}
|
catch (Exception ex)
|
{
|
// 处理运行时异常
|
Debug.WriteLine($"执行映射动作失败: {ex.Message}");
|
}
|
}
|
}
|
}
|
|
public void Dispose()
|
{
|
foreach (var list in _compiledMappings.Values)
|
{
|
list.Clear();
|
}
|
_compiledMappings.Clear();
|
}
|
|
public void ClearMappings()
|
{
|
_compiledMappings.Clear();
|
}
|
|
public void RemoveProcessMappings(string processName)
|
{
|
_compiledMappings.TryRemove(processName, out _);
|
}
|
}
|
|
// 辅助扩展方法,用于调试
|
public static class ExpressionTreeDebugExtensions
|
{
|
public static void DebugMemberAccess(this ExpressionTreeMappingEngine engine, IProcess testProcess)
|
{
|
var processType = typeof(IProcess);
|
|
Debug.WriteLine("=== IProcess 成员调试信息 ===");
|
Debug.WriteLine("字段:");
|
foreach (var field in processType.GetFields(BindingFlags.Public | BindingFlags.Instance))
|
{
|
Debug.WriteLine($" {field.Name} : {field.FieldType.Name}");
|
}
|
|
Debug.WriteLine("属性:");
|
foreach (var prop in processType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
{
|
Debug.WriteLine($" {prop.Name} : {prop.PropertyType.Name}");
|
}
|
|
// 测试具体成员访问
|
try
|
{
|
var processExpr = Expression.Parameter(typeof(IProcess), "p");
|
var outputImageExpr = Expression.Field(processExpr, "OutputImage");
|
var lambda = Expression.Lambda<Func<IProcess, object>>(outputImageExpr, processExpr).Compile();
|
var result = lambda(testProcess);
|
Debug.WriteLine($"OutputImage 字段访问成功: {result ?? "null"}");
|
}
|
catch (Exception ex)
|
{
|
Debug.WriteLine($"OutputImage 字段访问失败: {ex.Message}");
|
}
|
}
|
}
|
}
|