8.4 KiB
XP.ReportEngine 使用指南 | Usage Guidance
1. 概述
本文档说明如何在 XplorePlane 项目中使用 XP.ReportEngine 模块生成 PDF 检测报告。
模块提供两种调用方式:
- 推荐:通过
IReportService门面接口(一行调用,自动处理所有细节) - 高级:直接使用底层管线接口(
IReportGenerator、IReportDataAdapter等)
2. 前置条件
2.1 项目引用
在调用方项目的 .csproj 中添加项目引用:
<ProjectReference Include="..\XP.ReportEngine\XP.ReportEngine.csproj" />
2.2 模块注册
确保 ReportEngineModule 已在 XP.App 的模块目录中注册。模块初始化时会自动:
- 注册所有服务到 DI 容器
- 注册多语言资源到 Fallback Chain
- 后台执行引擎预热(字体加载 + JIT 编译)
2.3 配置文件
在 App.config 中添加报告引擎配置项,参见 Documents/App.config.example。
3. 通过 IReportService 生成报告(推荐)
3.1 基本用法
using XP.ReportEngine.Interfaces;
using XP.ReportEngine.Models;
public class InspectionService
{
private readonly IReportService _reportService;
public InspectionService(IReportService reportService)
{
_reportService = reportService;
}
public async Task<string> GenerateInspectionReport(
List<ProcessorOutput> processorOutputs,
string productName,
string operatorName)
{
var request = new ReportRequest
{
// 必填:处理器输出数据
ProcessorOutputs = processorOutputs,
// 必填:报告元数据
Metadata = new ReportMetadata
{
SampleName = productName,
OperatorName = operatorName,
InspectionDate = DateTime.Now,
Description = "BGA 焊球气泡率检测"
},
// 可选:文件名占位符参数(用于 FileNamePattern)
FileNameParameters = new Dictionary<string, string>
{
["ProductName"] = productName,
["ProductCode"] = "PCBA-X100",
["WorkpieceSN"] = "SN20250001",
["DeviceId"] = "XP-CT-001",
["MachineId"] = "MC01",
["Result"] = "Pass"
}
};
var result = await _reportService.GenerateAsync(request);
if (result.IsSuccess)
{
return result.OutputFilePath; // PDF 文件路径
}
else
{
throw new Exception($"报告生成失败: {result.ErrorMessage}");
}
}
}
3.2 指定输出路径
var request = new ReportRequest
{
ProcessorOutputs = outputs,
Metadata = metadata,
// 指定输出路径后,不再使用 ReportConfig 中的 OutputDirectory 和 FileNamePattern
OutputFilePath = @"D:\CustomPath\MyReport.pdf"
};
3.3 注入额外图像
var request = new ReportRequest
{
ProcessorOutputs = outputs,
Metadata = metadata,
AdditionalImages = new Dictionary<string, ImageData>
{
// 工件整体图(首页显示)
["workpieceImage"] = new ImageData
{
SourceType = ImageSourceType.FilePath,
FilePath = @"D:\Images\workpiece.png"
},
// 自定义图像
["customImage"] = new ImageData
{
SourceType = ImageSourceType.Bytes,
Bytes = imageBytes
}
}
};
3.4 注入自定义属性
var request = new ReportRequest
{
ProcessorOutputs = outputs,
Metadata = metadata,
// 自定义属性会合并到 ReportContext.Properties,可在模板中通过 ${key} 绑定
CustomProperties = new Dictionary<string, object>
{
["batchNumber"] = "BATCH-2025-001",
["inspectionStation"] = "Station-A"
}
};
4. ReportRequest 完整字段说明
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
ProcessorOutputs |
List<ProcessorOutput> |
是 | 处理器输出数据列表 |
Metadata |
ReportMetadata |
是 | 报告元数据 |
OutputFilePath |
string |
否 | 输出路径,为空时自动生成 |
FileNameParameters |
Dictionary<string, string> |
否 | 文件名占位符参数 |
AdditionalImages |
Dictionary<string, ImageData> |
否 | 额外图像数据 |
CustomProperties |
Dictionary<string, object> |
否 | 自定义属性 |
ReportMetadata 字段
| 字段 | 类型 | 说明 |
|---|---|---|
ReportId |
string |
报告编号(为空时自动生成 RPT-yyyyMMdd-NNN) |
InspectionDate |
DateTime |
检测日期 |
SampleName |
string |
样品/产品名称 |
OperatorName |
string |
操作员 |
Description |
string |
描述信息 |
ProcessorOutput 字段
| 字段 | 类型 | 说明 |
|---|---|---|
ProcessorType |
string |
处理器类型标识 |
OutputData |
Dictionary<string, object> |
输出数据字典 |
AnnotatedImage |
ImageData |
关联的已标注图像 |
支持的 ProcessorType:
LineMeasurementProcessor— 线测量BgaVoidRateProcessor— BGA 气泡率VoidMeasurementProcessor— 空隙测量FillRateProcessor— 通孔填锡率
5. ReportServiceResult 结果说明
| 字段 | 类型 | 说明 |
|---|---|---|
IsSuccess |
bool |
是否成功 |
OutputFilePath |
string |
输出文件路径(成功时有值) |
ReportId |
string |
报告编号 |
ErrorMessage |
string |
错误信息(失败时有值) |
Exception |
Exception |
异常对象(失败时有值) |
6. 在 ViewModel 中使用(带 UI 交互)
如果需要进度条、文件对话框等 UI 交互,参考 ReportDemoViewModel.cs 的实现模式:
// 在后台线程执行,避免阻塞 UI
var genResult = await Task.Run(() => _reportService.GenerateAsync(request));
关键点:
IReportService.GenerateAsync内部是 CPU 密集型操作(PDF 渲染)- 必须用
Task.Run推到线程池,否则会阻塞 UI 线程 - 进度条、对话框等 UI 逻辑留在 ViewModel 中
7. 高级用法:直接使用底层管线
适用于需要自定义管线某个阶段的场景:
// 1. 数据适配
var adapter = container.Resolve<IReportDataAdapter>();
var context = adapter.Adapt(processorOutputs, metadata);
// 2. 手动修改上下文
context.Properties["customKey"] = "customValue";
context.Images["myImage"] = new ImageData { ... };
// 3. 调用管线生成
var generator = container.Resolve<IReportGenerator>();
var options = new ReportGenerationOptions
{
TemplatePath = "Templates/StandardReportTemplate.json",
OutputFilePath = @"D:\output.pdf",
Format = ReportOutputFormat.Pdf
};
var result = await generator.GenerateAsync(context, options);
8. 预热机制
模块启动时自动在后台执行预热,无需手动调用。如果需要在特定时机手动预热:
var reportService = container.Resolve<IReportService>();
await reportService.WarmUpAsync();
预热内容:
- iText7 程序集加载
- BouncyCastle 加密提供程序注册
- 微软雅黑字体文件读取和解析(约 5-6 秒)
- JSON 模板反序列化 JIT 编译
- 管线各阶段代码 JIT 编译
预热完成后,首次正式生成报告不会有额外延迟。
9. 线程安全
IReportService 内部使用 SemaphoreSlim(1, 1) 互斥锁,确保:
- 预热与正式生成不会并发执行
- 多个并发的
GenerateAsync调用会串行执行
这是因为 ITextPdfRenderer 的字体字段在每次渲染时重置,并发渲染会导致 iText7 的 "belongs to other PDF document" 错误。
10. 常见问题
Q: 首次生成报告很慢?
A: 正常现象。首次需要加载字体(~5s)+ 字体子集化(~1.2s)。预热机制会在应用启动时后台完成字体加载,用户首次操作时只需等待子集化时间。
Q: PDF 中图像显示为"无图像"占位矩形?
A: 检查 ProcessorOutput.AnnotatedImage 或 ReportRequest.AdditionalImages 中的图像路径是否正确、文件是否存在。
Q: 报告中某些字段为空?
A: 检查 ProcessorOutput.OutputData 中的键名是否与 ProcessorDataAdapter 中的映射一致(区分大小写)。
Q: 如何自定义报告模板?
A: 修改 Templates/StandardReportTemplate.json,参考 Documents/XP.ReportEngineModelDefine.md 中的模板结构定义。