using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using XP.Common.Logging.Interfaces;
using XP.ReportEngine.Interfaces;
using XP.ReportEngine.Models;
namespace XP.ReportEngine.Services
{
///
/// 处理器数据适配器实现 | Processor data adapter implementation
/// 将 XP.ImageProcessing 的 ProcessorOutput 转换为 ReportContext
/// Converts XP.ImageProcessing ProcessorOutput to ReportContext
///
public class ProcessorDataAdapter : IReportDataAdapter
{
private readonly ILoggerService _logger;
// 处理器类型常量 | Processor type constants
private const string LineMeasurementProcessor = "LineMeasurementProcessor";
private const string BgaVoidRateProcessor = "BgaVoidRateProcessor";
private const string VoidMeasurementProcessor = "VoidMeasurementProcessor";
private const string FillRateProcessor = "FillRateProcessor";
public ProcessorDataAdapter(ILoggerService logger)
{
_logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
}
///
/// 将处理器输出数据适配为报告上下文 | Adapt processor output data to report context
///
public ReportContext Adapt(List processorOutputs, ReportMetadata metadata)
{
if (processorOutputs == null) throw new ArgumentNullException(nameof(processorOutputs));
if (metadata == null) throw new ArgumentNullException(nameof(metadata));
_logger.Info("开始数据适配,处理器数量: {Count} | Starting data adaptation, processor count: {Count}", processorOutputs.Count);
var context = new ReportContext
{
Metadata = metadata,
ResultGroups = new List(),
Images = new Dictionary(),
Properties = new Dictionary()
};
// 多处理器输出聚合逻辑:每个 ProcessorOutput 生成一个 InspectionResultGroup | Aggregation: each ProcessorOutput becomes one InspectionResultGroup
for (var i = 0; i < processorOutputs.Count; i++)
{
var output = processorOutputs[i];
if (output == null)
{
_logger.Warn("处理器输出为 null,索引: {Index},已跳过 | Processor output is null at index: {Index}, skipped", i);
continue;
}
var group = AdaptProcessorOutput(output, i);
context.ResultGroups.Add(group);
// 将结果数据扁平化到 Properties(供模板 ${key} 表达式绑定)
// Flatten result data to Properties (for template ${key} expression binding)
if (group.Data != null)
{
foreach (var kvp in group.Data)
{
context.Properties[kvp.Key] = kvp.Value;
}
}
// 将 Classification 也放入 Properties | Also put Classification into Properties
if (!string.IsNullOrEmpty(group.Classification))
{
context.Properties["classification"] = group.Classification;
}
// 将表格数据以模板期望的 dataKey 存入 Properties
// Store table data with template-expected dataKey into Properties
if (group.TableRows != null && group.TableRows.Count > 0)
{
var tableKey = GetTableDataKey(output.ProcessorType);
if (!string.IsNullOrEmpty(tableKey))
{
context.Properties[tableKey] = group.TableRows;
}
}
// 关联标注图像(使用模板期望的 dataKey)
// Associate annotated image (using template-expected dataKey)
if (output.AnnotatedImage != null)
{
var imageKey = GetImageDataKey(output.ProcessorType);
context.Images[imageKey] = output.AnnotatedImage;
// 同时保留原始键名以兼容其他调用方 | Also keep original key for other callers
var originalKey = $"{output.ProcessorType}_{i}_annotated";
context.Images[originalKey] = output.AnnotatedImage;
}
}
_logger.Info("数据适配完成,生成 {Count} 个结果分组 | Data adaptation completed, generated {Count} result groups", context.ResultGroups.Count);
return context;
}
///
/// 根据处理器类型分发适配逻辑 | Dispatch adaptation logic by processor type
///
private InspectionResultGroup AdaptProcessorOutput(ProcessorOutput output, int index)
{
var sourceId = $"{output.ProcessorType}_{index}";
return output.ProcessorType switch
{
LineMeasurementProcessor => AdaptLineMeasurement(output, sourceId),
BgaVoidRateProcessor => AdaptBgaVoidRate(output, sourceId),
VoidMeasurementProcessor => AdaptVoidMeasurement(output, sourceId),
FillRateProcessor => AdaptFillRate(output, sourceId),
_ => AdaptGeneric(output, sourceId)
};
}
#region LineMeasurementProcessor 适配 | LineMeasurementProcessor Adaptation
///
/// 适配线测量处理器输出 | Adapt line measurement processor output
/// 提取 MeasurementType、Point1、Point2、PixelDistance、ActualDistance、Unit、Angle
///
private InspectionResultGroup AdaptLineMeasurement(ProcessorOutput output, string sourceId)
{
_logger.Debug("适配 LineMeasurementProcessor 输出,SourceId: {SourceId} | Adapting LineMeasurementProcessor output, SourceId: {SourceId}", sourceId);
var data = output.OutputData ?? new Dictionary();
var group = new InspectionResultGroup
{
ProcessorType = LineMeasurementProcessor,
SourceId = sourceId,
Classification = string.Empty,
Data = new Dictionary
{
["measurementType"] = GetValueOrDefault(data, "MeasurementType", string.Empty),
["point1"] = GetValueOrDefault