diff --git a/XP.ReportEngine/ReportEngineModule.cs b/XP.ReportEngine/ReportEngineModule.cs
index 1665670..aae3f36 100644
--- a/XP.ReportEngine/ReportEngineModule.cs
+++ b/XP.ReportEngine/ReportEngineModule.cs
@@ -1,19 +1,71 @@
using Prism.Ioc;
using Prism.Modularity;
-using XP.ReportEngine.Views;
+using System.Resources;
+using XP.Common.Localization;
+using XP.Common.Localization.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Services;
namespace XP.ReportEngine
{
+ ///
+ /// 报告引擎模块 | Report Engine Module
+ /// Prism 模块入口,注册报告生成相关服务到 DI 容器
+ /// Prism module entry, registers report generation services to DI container
+ ///
+ [Module(ModuleName = "ReportEngineModule")]
public class ReportEngineModule : IModule
{
+ ///
+ /// 模块初始化 | Module initialization
+ /// 注册模块级多语言资源源 | Register module-level localization resource source
+ ///
public void OnInitialized(IContainerProvider containerProvider)
{
+ // 注册模块级多语言资源到 Fallback Chain | Register module-level localization resources to Fallback Chain
+ var localizationService = containerProvider.Resolve();
+ var resourceManager = new ResourceManager(
+ "XP.ReportEngine.Resources.Resources",
+ typeof(ReportEngineModule).Assembly);
+ localizationService.RegisterResourceSource("XP.ReportEngine", resourceManager);
+ // 初始化 LocalizationHelper,使其通过 ILocalizationService 获取字符串(支持 Fallback Chain)
+ // Initialize LocalizationHelper to use ILocalizationService for string lookup (supports Fallback Chain)
+ LocalizationHelper.Initialize(localizationService);
+
+ System.Console.WriteLine("[ReportEngineModule] 模块已初始化 | Module initialized");
}
+ ///
+ /// 注册类型到 DI 容器 | Register types to DI container
+ ///
public void RegisterTypes(IContainerRegistry containerRegistry)
{
+ // 注册报告生成器(瞬态)| Register report generator (transient)
+ containerRegistry.Register();
+ // 注册报告生成器工厂(单例)| Register report generator factory (singleton)
+ containerRegistry.RegisterSingleton();
+
+ // 注册模板引擎(瞬态)| Register template engine (transient)
+ containerRegistry.Register();
+
+ // 注册数据绑定器(瞬态)| Register data binder (transient)
+ containerRegistry.Register();
+
+ // 注册排版引擎(瞬态)| Register layout engine (transient)
+ containerRegistry.Register();
+
+ // 注册 PDF 渲染器(瞬态)| Register PDF renderer (transient)
+ containerRegistry.Register();
+
+ // 注册数据适配器(瞬态)| Register data adapter (transient)
+ containerRegistry.Register();
+
+ // 注册报告编号生成器(单例,维护每日计数器状态)| Register report ID generator (singleton, maintains daily counter state)
+ containerRegistry.RegisterSingleton();
+
+ System.Console.WriteLine("[ReportEngineModule] 类型注册完成 | Type registration completed");
}
}
-}
\ No newline at end of file
+}
diff --git a/XP.ReportEngine/Resources/Resources.en-US.resx b/XP.ReportEngine/Resources/Resources.en-US.resx
index 459ff73..68d1475 100644
--- a/XP.ReportEngine/Resources/Resources.en-US.resx
+++ b/XP.ReportEngine/Resources/Resources.en-US.resx
@@ -58,4 +58,184 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Inspection Report
+
+
+ Inspection Date
+
+
+ Sample Name
+
+
+ Operator
+
+
+ Inspection Summary
+
+
+ Report ID
+
+
+ Description
+
+
+
+ PASS
+
+
+ FAIL
+
+
+
+ Line Measurement
+
+
+ BGA Void Rate
+
+
+ Void Measurement
+
+
+ Via Fill Rate
+
+
+
+ No.
+
+
+ Void Rate
+
+
+ Classification
+
+
+ Area
+
+
+ Area %
+
+
+ Center X
+
+
+ Center Y
+
+
+
+ Measurement Type
+
+
+ Distance
+
+
+ Unit
+
+
+ Angle
+
+
+ Fill Rate
+
+
+ Void Rate
+
+
+ Limit
+
+
+
+ Total Defects
+
+
+ Pass Count
+
+
+ Fail Count
+
+
+ Overall Result
+
+
+
+ No Image
+
+
+
+ Inspection Report
+
+
+ Measurement Data
+
+
+ BGA Solder Ball Inspection
+
+
+ Void Inspection
+
+
+ Via Fill Rate Inspection
+
+
+
+ Ball Count
+
+
+ Total Ball Area
+
+
+ Total Void Area
+
+
+ Void Rate Limit
+
+
+ Ball No.
+
+
+
+ ROI Area
+
+
+ Total Void Area
+
+
+ Void Count
+
+
+ Max Void Area
+
+
+ Void Rate Limit
+
+
+
+ Fill Rate
+
+
+ Full Distance
+
+
+ Fill Distance
+
+
+ THT Limit
+
+
+
+ Point 1
+
+
+ Point 2
+
+
+ Result
+
+
+ Inspection Type
+
+
+ Status
+
diff --git a/XP.ReportEngine/Resources/Resources.resx b/XP.ReportEngine/Resources/Resources.resx
index 44e9f97..e248a76 100644
--- a/XP.ReportEngine/Resources/Resources.resx
+++ b/XP.ReportEngine/Resources/Resources.resx
@@ -1,64 +1,5 @@
-
@@ -117,4 +58,184 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 检测报告
+
+
+ 检测日期
+
+
+ 样品名称
+
+
+ 操作员
+
+
+ 检测摘要
+
+
+ 报告编号
+
+
+ 描述
+
+
+
+ 通过
+
+
+ 不通过
+
+
+
+ 距离测量
+
+
+ BGA 气泡率
+
+
+ 空隙测量
+
+
+ 通孔填锡率
+
+
+
+ 序号
+
+
+ 气泡率
+
+
+ 分类结果
+
+
+ 面积
+
+
+ 面积百分比
+
+
+ 中心 X
+
+
+ 中心 Y
+
+
+
+ 测量类型
+
+
+ 距离
+
+
+ 单位
+
+
+ 角度
+
+
+ 填锡率
+
+
+ 气泡率
+
+
+ 限值
+
+
+
+ 总缺陷数
+
+
+ 通过数量
+
+
+ 不通过数量
+
+
+ 总体结果
+
+
+
+ 无图像
+
+
+
+ 检测报告首页
+
+
+ 测量数据
+
+
+ BGA 焊球检测
+
+
+ 空隙检测
+
+
+ 通孔填锡检测
+
+
+
+ 焊球数量
+
+
+ 焊球总面积
+
+
+ 气泡总面积
+
+
+ 气泡率限值
+
+
+ 焊球序号
+
+
+
+ ROI 面积
+
+
+ 空隙总面积
+
+
+ 空隙数量
+
+
+ 最大空隙面积
+
+
+ 空隙率限值
+
+
+
+ 填锡率
+
+
+ 满填距离
+
+
+ 填充距离
+
+
+ THT 限值
+
+
+
+ 起点
+
+
+ 终点
+
+
+ 结果
+
+
+ 检测类型
+
+
+ 状态
+
diff --git a/XP.ReportEngine/Resources/Resources.zh-CN.resx b/XP.ReportEngine/Resources/Resources.zh-CN.resx
index 459ff73..e248a76 100644
--- a/XP.ReportEngine/Resources/Resources.zh-CN.resx
+++ b/XP.ReportEngine/Resources/Resources.zh-CN.resx
@@ -58,4 +58,184 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 检测报告
+
+
+ 检测日期
+
+
+ 样品名称
+
+
+ 操作员
+
+
+ 检测摘要
+
+
+ 报告编号
+
+
+ 描述
+
+
+
+ 通过
+
+
+ 不通过
+
+
+
+ 距离测量
+
+
+ BGA 气泡率
+
+
+ 空隙测量
+
+
+ 通孔填锡率
+
+
+
+ 序号
+
+
+ 气泡率
+
+
+ 分类结果
+
+
+ 面积
+
+
+ 面积百分比
+
+
+ 中心 X
+
+
+ 中心 Y
+
+
+
+ 测量类型
+
+
+ 距离
+
+
+ 单位
+
+
+ 角度
+
+
+ 填锡率
+
+
+ 气泡率
+
+
+ 限值
+
+
+
+ 总缺陷数
+
+
+ 通过数量
+
+
+ 不通过数量
+
+
+ 总体结果
+
+
+
+ 无图像
+
+
+
+ 检测报告首页
+
+
+ 测量数据
+
+
+ BGA 焊球检测
+
+
+ 空隙检测
+
+
+ 通孔填锡检测
+
+
+
+ 焊球数量
+
+
+ 焊球总面积
+
+
+ 气泡总面积
+
+
+ 气泡率限值
+
+
+ 焊球序号
+
+
+
+ ROI 面积
+
+
+ 空隙总面积
+
+
+ 空隙数量
+
+
+ 最大空隙面积
+
+
+ 空隙率限值
+
+
+
+ 填锡率
+
+
+ 满填距离
+
+
+ 填充距离
+
+
+ THT 限值
+
+
+
+ 起点
+
+
+ 终点
+
+
+ 结果
+
+
+ 检测类型
+
+
+ 状态
+
diff --git a/XP.ReportEngine/Resources/Resources.zh-TW.resx b/XP.ReportEngine/Resources/Resources.zh-TW.resx
index 459ff73..0e12d4d 100644
--- a/XP.ReportEngine/Resources/Resources.zh-TW.resx
+++ b/XP.ReportEngine/Resources/Resources.zh-TW.resx
@@ -58,4 +58,184 @@
System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ 檢測報告
+
+
+ 檢測日期
+
+
+ 樣品名稱
+
+
+ 操作員
+
+
+ 檢測摘要
+
+
+ 報告編號
+
+
+ 描述
+
+
+
+ 通過
+
+
+ 不通過
+
+
+
+ 距離測量
+
+
+ BGA 氣泡率
+
+
+ 空隙測量
+
+
+ 通孔填錫率
+
+
+
+ 序號
+
+
+ 氣泡率
+
+
+ 分類結果
+
+
+ 面積
+
+
+ 面積百分比
+
+
+ 中心 X
+
+
+ 中心 Y
+
+
+
+ 測量類型
+
+
+ 距離
+
+
+ 單位
+
+
+ 角度
+
+
+ 填錫率
+
+
+ 氣泡率
+
+
+ 限值
+
+
+
+ 總缺陷數
+
+
+ 通過數量
+
+
+ 不通過數量
+
+
+ 總體結果
+
+
+
+ 無圖像
+
+
+
+ 檢測報告首頁
+
+
+ 測量數據
+
+
+ BGA 焊球檢測
+
+
+ 空隙檢測
+
+
+ 通孔填錫檢測
+
+
+
+ 焊球數量
+
+
+ 焊球總面積
+
+
+ 氣泡總面積
+
+
+ 氣泡率限值
+
+
+ 焊球序號
+
+
+
+ ROI 面積
+
+
+ 空隙總面積
+
+
+ 空隙數量
+
+
+ 最大空隙面積
+
+
+ 空隙率限值
+
+
+
+ 填錫率
+
+
+ 滿填距離
+
+
+ 填充距離
+
+
+ THT 限值
+
+
+
+ 起點
+
+
+ 終點
+
+
+ 結果
+
+
+ 檢測類型
+
+
+ 狀態
+
diff --git a/XP.ReportEngine/Services/PdfReportGenerator.cs b/XP.ReportEngine/Services/PdfReportGenerator.cs
new file mode 100644
index 0000000..9eba04f
--- /dev/null
+++ b/XP.ReportEngine/Services/PdfReportGenerator.cs
@@ -0,0 +1,134 @@
+using System;
+using System.IO;
+using System.Threading.Tasks;
+using XP.Common.Logging.Interfaces;
+using XP.ReportEngine.Interfaces;
+using XP.ReportEngine.Models;
+
+namespace XP.ReportEngine.Services
+{
+ ///
+ /// PDF 报告生成器实现 | PDF report generator implementation
+ /// 协调管线各阶段:模板加载 → 数据绑定 → 排版 → 渲染 → 保存
+ /// Orchestrates pipeline phases: template loading → data binding → layout → rendering → saving
+ ///
+ public class PdfReportGenerator : IReportGenerator
+ {
+ private readonly ILoggerService _logger;
+ private readonly ITemplateEngine _templateEngine;
+ private readonly IDataBinder _dataBinder;
+ private readonly ILayoutEngine _layoutEngine;
+ private readonly IPdfRenderer _pdfRenderer;
+
+ ///
+ /// 构造函数 | Constructor
+ ///
+ /// 日志服务 | Logger service
+ /// 模板引擎 | Template engine
+ /// 数据绑定器 | Data binder
+ /// 排版引擎 | Layout engine
+ /// PDF 渲染器 | PDF renderer
+ public PdfReportGenerator(
+ ILoggerService logger,
+ ITemplateEngine templateEngine,
+ IDataBinder dataBinder,
+ ILayoutEngine layoutEngine,
+ IPdfRenderer pdfRenderer)
+ {
+ _logger = logger?.ForModule() ?? throw new ArgumentNullException(nameof(logger));
+ _templateEngine = templateEngine ?? throw new ArgumentNullException(nameof(templateEngine));
+ _dataBinder = dataBinder ?? throw new ArgumentNullException(nameof(dataBinder));
+ _layoutEngine = layoutEngine ?? throw new ArgumentNullException(nameof(layoutEngine));
+ _pdfRenderer = pdfRenderer ?? throw new ArgumentNullException(nameof(pdfRenderer));
+ }
+
+ ///
+ /// 异步生成 PDF 报告 | Generate PDF report asynchronously
+ /// 执行完整管线:模板加载 → 验证 → 数据绑定 → 排版计算 → PDF 渲染 → 文件保存(可选)
+ /// Executes full pipeline: template load → validate → data bind → layout → PDF render → save (optional)
+ ///
+ /// 报告上下文数据 | Report context data
+ /// 生成选项 | Generation options
+ /// 生成结果 | Generation result
+ public async Task GenerateAsync(ReportContext context, ReportGenerationOptions options)
+ {
+ try
+ {
+ _logger.Info("报告生成管线开始 | Report generation pipeline started");
+
+ // 阶段 1:加载模板 | Phase 1: Load template
+ _logger.Info("阶段 1:加载模板 | Phase 1: Loading template");
+ var template = _templateEngine.LoadTemplate(options.TemplatePath);
+ if (template == null)
+ {
+ var errorMsg = $"模板文件未找到: {options.TemplatePath}";
+ _logger.Error(null, "模板加载失败: {Path} | Template loading failed: {Path}", options.TemplatePath);
+ return ReportResult.Failure(errorMsg);
+ }
+
+ var validation = _templateEngine.Validate(template);
+ if (!validation.IsValid)
+ {
+ var errorMsg = $"模板验证失败: {validation.ErrorMessage}";
+ _logger.Error(null, "模板验证失败: {Message} | Template validation failed: {Message}", validation.ErrorMessage);
+ return ReportResult.Failure(errorMsg);
+ }
+ _logger.Info("阶段 1 完成:模板加载成功 | Phase 1 completed: Template loaded successfully");
+
+ // 阶段 2:数据绑定 | Phase 2: Data binding
+ _logger.Info("阶段 2:数据绑定 | Phase 2: Data binding");
+ var boundTemplate = _dataBinder.Bind(template, context);
+ _logger.Info("阶段 2 完成:数据绑定成功 | Phase 2 completed: Data binding successful");
+
+ // 阶段 3:排版计算 | Phase 3: Layout calculation
+ _logger.Info("阶段 3:排版计算 | Phase 3: Layout calculation");
+ var pages = _layoutEngine.CalculateLayout(boundTemplate, options);
+ _logger.Info("阶段 3 完成:排版计算成功,共 {PageCount} 页 | Phase 3 completed: Layout calculated, {PageCount} pages", pages.Count);
+
+ // 阶段 4:PDF 渲染 | Phase 4: PDF rendering
+ _logger.Info("阶段 4:PDF 渲染 | Phase 4: PDF rendering");
+ var stream = _pdfRenderer.Render(pages, options);
+ _logger.Info("阶段 4 完成:PDF 渲染成功 | Phase 4 completed: PDF rendering successful");
+
+ // 阶段 5:保存文件(可选)| Phase 5: Save file (optional)
+ if (!string.IsNullOrEmpty(options.OutputFilePath))
+ {
+ _logger.Info("阶段 5:保存文件 | Phase 5: Saving file");
+ await SaveToFileAsync(stream, options.OutputFilePath);
+ _logger.Info("阶段 5 完成:文件保存成功 {Path} | Phase 5 completed: File saved successfully {Path}", options.OutputFilePath);
+ }
+
+ _logger.Info("报告生成管线完成 | Report generation pipeline completed");
+ return ReportResult.Success(stream);
+ }
+ catch (Exception ex)
+ {
+ _logger.Error(ex, "报告生成失败 | Report generation failed: {Message}", ex.Message);
+ return ReportResult.Failure($"报告生成过程中发生错误: {ex.Message}", ex);
+ }
+ }
+
+ ///
+ /// 将 MemoryStream 保存到文件 | Save MemoryStream to file
+ ///
+ /// PDF 内存流 | PDF memory stream
+ /// 输出文件路径 | Output file path
+ private async Task SaveToFileAsync(MemoryStream stream, string filePath)
+ {
+ // 确保输出目录存在 | Ensure output directory exists
+ var directory = Path.GetDirectoryName(filePath);
+ if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory))
+ {
+ Directory.CreateDirectory(directory);
+ }
+
+ // 重置流位置后写入文件 | Reset stream position before writing to file
+ stream.Position = 0;
+ using var fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.None);
+ await stream.CopyToAsync(fileStream);
+
+ // 重置流位置以便后续使用 | Reset stream position for subsequent use
+ stream.Position = 0;
+ }
+ }
+}
diff --git a/XP.ReportEngine/Services/ProcessorDataAdapter.cs b/XP.ReportEngine/Services/ProcessorDataAdapter.cs
new file mode 100644
index 0000000..b525767
--- /dev/null
+++ b/XP.ReportEngine/Services/ProcessorDataAdapter.cs
@@ -0,0 +1,472 @@
+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);
+
+ // 关联标注图像 | Associate annotated image
+ if (output.AnnotatedImage != null)
+ {
+ var imageKey = $"{output.ProcessorType}_{i}_annotated";
+ context.Images[imageKey] = 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