Files
XplorePlane/XP.ReportEngine/Documents/Guidance.md
T

8.4 KiB
Raw Blame History

XP.ReportEngine 使用指南 | Usage Guidance

1. 概述

本文档说明如何在 XplorePlane 项目中使用 XP.ReportEngine 模块生成 PDF 检测报告。

模块提供两种调用方式:

  • 推荐:通过 IReportService 门面接口(一行调用,自动处理所有细节)
  • 高级:直接使用底层管线接口(IReportGeneratorIReportDataAdapter 等)

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.AnnotatedImageReportRequest.AdditionalImages 中的图像路径是否正确、文件是否存在。

Q: 报告中某些字段为空?

A: 检查 ProcessorOutput.OutputData 中的键名是否与 ProcessorDataAdapter 中的映射一致(区分大小写)。

Q: 如何自定义报告模板?

A: 修改 Templates/StandardReportTemplate.json,参考 Documents/XP.ReportEngineModelDefine.md 中的模板结构定义。