VisionPro之CogSerializer序列化与持久化工具

文章目录

  • [1. 功能介绍](#1. 功能介绍)
    • [1.1 核心特性](#1.1 核心特性)
  • [2. 核心API详解](#2. 核心API详解)
    • [2.1 文件读写方法](#2.1 文件读写方法)
      • [2.1.1 `SaveObjectToFile`---保存对象到文件](#2.1.1 SaveObjectToFile—保存对象到文件)
      • [2.1.2 `LoadObjectFromFile`---从文件加载对象](#2.1.2 LoadObjectFromFile—从文件加载对象)
    • [2.2 内存流读写方法](#2.2 内存流读写方法)
      • [2.2.1 `SaveObjectToStream`---保存对象到内存流](#2.2.1 SaveObjectToStream—保存对象到内存流)
      • [2.2.2 `LoadObjectFromStream`---从内存流加载对象](#2.2.2 LoadObjectFromStream—从内存流加载对象)
    • [2.3 深度复制方法](#2.3 深度复制方法)
      • [2.3.1 `DeepCopyObject`---深度复制(官方推荐)](#2.3.1 DeepCopyObject—深度复制(官方推荐))
      • [2.3.2 `CogSerializationOptionsConstants`枚举详解](#2.3.2 CogSerializationOptionsConstants枚举详解)
        • [2.3.2.1 选项组合使用](#2.3.2.1 选项组合使用)
        • [2.3.2.2 选项选择建议](#2.3.2.2 选项选择建议)
  • [3. 代码示例](#3. 代码示例)
    • [3.1 示例 1:基本的保存与加载](#3.1 示例 1:基本的保存与加载)
    • [3.2 示例 2:多粒度保存策略](#3.2 示例 2:多粒度保存策略)
    • [3.3 示例 3:使用流进行内存复制与多版本管理](#3.3 示例 3:使用流进行内存复制与多版本管理)
    • [3.4 示例 4:批量配置文件管理器](#3.4 示例 4:批量配置文件管理器)
    • [3.5 示例 5:深度复制在A/B测试中的应用](#3.5 示例 5:深度复制在A/B测试中的应用)
    • [3.6 示例 6:CogAcqFifoTool 相机配置的保存与加载](#3.6 示例 6:CogAcqFifoTool 相机配置的保存与加载)
    • [3.7 示例 7:异常处理与文件校验](#3.7 示例 7:异常处理与文件校验)
  • 4.CogSerializer补充示例集
    • [4.1 ToolBlock 输入输出连线的序列化与恢复](#4.1 ToolBlock 输入输出连线的序列化与恢复)
    • [4.2 模板训练结果的持久化与复用](#4.2 模板训练结果的持久化与复用)
    • [4.3 Fixture定位工具的配置持久化](#4.3 Fixture定位工具的配置持久化)
    • [4.4 多相机配置管理](#4.4 多相机配置管理)
    • [4.5 ToolBlock运行结果的序列化与分析](#4.5 ToolBlock运行结果的序列化与分析)
    • [4.6 标定数据的保存与恢复](#4.6 标定数据的保存与恢复)
    • [4.7 配置热更新(运行时动态替换)](#4.7 配置热更新(运行时动态替换))
    • [4.8 ToolBlock高级脚本中的序列化应用](#4.8 ToolBlock高级脚本中的序列化应用)
    • [4.9 配置版本管理与差异对比](#4.9 配置版本管理与差异对比)
    • [4.10 完整的视觉检测流水线](#4.10 完整的视觉检测流水线)
  • 5.相关类速查表
    • [5.1 核心序列化相关类](#5.1 核心序列化相关类)
    • [5.2 常用可序列化对象](#5.2 常用可序列化对象)
    • [5.3 VisionPro工具与命名空间对照](#5.3 VisionPro工具与命名空间对照)
  • [6. CogMisc实用工具方法](#6. CogMisc实用工具方法)
  • 7.坐标空间特殊符号
  • [8. 附录](#8. 附录)
    • [8.1 常用using语句](#8.1 常用using语句)
    • [8.2 一句话速查](#8.2 一句话速查)

1. 功能介绍

CogSerializer是VisionPro 命名空间 Cognex.VisionPro 下的静态工具类,专门用于 VisionPro 复杂对象图的深度持久化。

它不是通用的 .NET 序列化器,而是Cognex为自家库深度定制的序列化方案,能递归处理 CogToolBlock 内部包含的整套工具链、图形接口、数据绑定甚至自定义脚本中的扩展属性。

1.1 核心特性

特性 说明
命名空间 Cognex.VisionPro
类类型 静态类(不可实例化)
文件格式 .vpp
序列化机制 基于 .NET Binary Formatter,深度定制
核心能力 文件/流 读写、深度复制、多粒度选项控制

重要版本提示CogToolBlockCogToolGroup 自身的 Clone() 方法在构造工具间数据连接时存在缺陷------克隆对象内的工具间链接不会正确重建。

康耐视官方文档明确指出克隆功能无法正常工作,并建议开发者改为使用 CogSerializer.DeepCopyObject() 来实现深度复制。

2. 核心API详解

2.1 文件读写方法

2.1.1 SaveObjectToFile---保存对象到文件

csharp 复制代码
public static void SaveObjectToFile(
    object obj,
    string path,
    IFormatter formatter = null,
    CogSerializationOptionsConstants options = CogSerializationOptionsConstants.Minimum
)
参数 类型 说明
obj object 要保存的 VisionPro 对象(如 CogToolBlockCogPMAlignPattern 等)
path string 目标文件路径,通常以 .vpp 为扩展名
formatter IFormatter 序列化格式器,传 null 使用默认的 BinaryFormatter
options CogSerializationOptionsConstants 序列化粒度选项,默认 Minimum

2.1.2 LoadObjectFromFile---从文件加载对象

csharp 复制代码
public static object LoadObjectFromFile(string path)
参数 类型 说明
path string .vpp 文件路径

LoadObjectFromFile返回值object 类型,需要显式类型转换。

2.2 内存流读写方法

2.2.1 SaveObjectToStream---保存对象到内存流

csharp 复制代码
public static void SaveObjectToStream(object obj, Stream stream)

2.2.2 LoadObjectFromStream---从内存流加载对象

csharp 复制代码
public static object LoadObjectFromStream(Stream stream)

适用场景:内存中对象复制、多版本管理、网络传输等临时操作。

2.3 深度复制方法

2.3.1 DeepCopyObject---深度复制(官方推荐)

csharp 复制代码
 public static object DeepCopyObject(object obj, CogSerializationOptionsConstants optionBits)
参数 类型 说明
source object 源 VisionPro 对象

返回值:与源对象同类型的深度副本,保留完整的内部引用关系。

关键说明 :此方法是康耐视官方推荐的替代 Clone() 的方案。其内部实现等价于将对象序列化到内存流再反序列化回来,因此能正确处理复杂对象图中的交叉引用。

2.3.2 CogSerializationOptionsConstants枚举详解

此枚举定义在 Cognex.VisionPro 命名空间下,用于控制序列化的粒度。

枚举值 说明 适用场景
Minimum 0 仅序列化必要信息,省略默认值和冗余数据 日常配置保存(推荐)
All 1 序列化所有信息(包括默认值) 完整备份、调试分析
ExcludeDataBindings 2 排除所有数据绑定信息 配置迁移(绑定信息在新环境下会失效)
ExcludeGraphics 4 排除所有图形(Graphics)信息 仅保存参数逻辑
2.3.2.1 选项组合使用

枚举值支持位运算组合,可以同时排除多种信息:

csharp 复制代码
// 同时排除数据绑定和图形信息
var options = CogSerializationOptionsConstants.ExcludeDataBindings 
            | CogSerializationOptionsConstants.ExcludeGraphics;
CogSerializer.SaveObjectToFile(block, path, null, options);
2.3.2.2 选项选择建议
场景 推荐选项 原因
日常开发保存 Minimum 文件小、速度快
版本发布归档 All 完整保留所有信息
跨机器迁移配置 ExcludeDataBindings 避免绑定路径失效导致错误
仅保存算法参数 ExcludeGraphics 减少图形对象的序列化开销
跨版本升级 `ExcludeDataBindings ExcludeGraphics`

3. 代码示例

3.1 示例 1:基本的保存与加载

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro.PMAlign;

// ===== 创建并配置 ToolBlock =====
CogToolBlock block = new CogToolBlock();
block.Name = "InspectionBlock";

// 添加模板匹配工具
CogPMAlignTool pmaTool = new CogPMAlignTool();
pmaTool.Name = "PMAlign1";
block.Tools.Add(pmaTool);

// 添加更多工具并配置...
// block.Tools.Add(otherTool);

// ===== 保存到文件 =====
string savePath = @"D:\VisionConfigs\InspectionBlock.vpp";
CogSerializer.SaveObjectToFile(block, savePath);
Console.WriteLine($"配置已保存至: {savePath}");

// ===== 从文件加载 =====
CogToolBlock loadedBlock = CogSerializer.LoadObjectFromFile(savePath) as CogToolBlock;
if (loadedBlock != null)
{
    Console.WriteLine($"加载成功,工具数量: {loadedBlock.Tools.Count}");
    loadedBlock.Run();
}

3.2 示例 2:多粒度保存策略

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.PMAlign;
using Cognex.VisionPro.ToolGroup;
using System.IO;

// ===== 场景 1: 保存单个模板匹配工具的 Pattern =====
CogPMAlignTool pmaTool = new CogPMAlignTool();
// ... 配置并训练 ...
CogSerializer.SaveObjectToFile(
    pmaTool.Pattern, 
    @"D:\Patterns\MyPattern.vpp"
);

// ===== 场景 2: 保存完整 ToolGroup(带所有工具和内部连线) =====
CogToolGroup toolGroup = new CogToolGroup();
// ... 添加工具并建立连接 ...
CogSerializer.SaveObjectToFile(
    toolGroup, 
    @"D:\Configs\FullToolGroup.vpp",
    null, 
    CogSerializationOptionsConstants.All  // 完整保存
);

// ===== 场景 3: 仅保存参数逻辑(排除图形和绑定) =====
CogSerializer.SaveObjectToFile(
    toolGroup, 
    @"D:\Configs\ParamsOnly.vpp",
    null, 
    CogSerializationOptionsConstants.ExcludeDataBindings 
    | CogSerializationOptionsConstants.ExcludeGraphics
);

// ===== 场景 4: 跨机器迁移时排除绑定信息 =====
CogSerializer.SaveObjectToFile(
    toolGroup, 
    @"D:\Configs\MigrateSafe.vpp",
    null, 
    CogSerializationOptionsConstants.ExcludeDataBindings
);

3.3 示例 3:使用流进行内存复制与多版本管理

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System.IO;

CogToolBlock originalBlock = CogSerializer.LoadObjectFromFile(@"D:\Configs\Block.vpp") as CogToolBlock;

// ===== 方法 1: 使用 DeepCopyObject(推荐) =====
CogToolBlock copy1 = CogSerializer.DeepCopyObject(originalBlock) as CogToolBlock;
copy1.Name = "Copy1";

// ===== 方法 2: 手动使用流 =====
using (MemoryStream ms = new MemoryStream())
{
    // 保存到流
    CogSerializer.SaveObjectToStream(originalBlock, ms);
    
    // 从流加载(需要重置流位置)
    ms.Position = 0;
    CogToolBlock copy2 = CogSerializer.LoadObjectFromStream(ms) as CogToolBlock;
    copy2.Name = "Copy2";
}

// ===== 实际应用:创建运行前/运行后快照 =====
using (MemoryStream beforeRun = new MemoryStream())
{
    CogSerializer.SaveObjectToStream(originalBlock, beforeRun);
    
    originalBlock.Run();  // 执行视觉检测
    
    // 运行后可以对比 beforeRun 和当前状态
    beforeRun.Position = 0;
    CogToolBlock snapshot = CogSerializer.LoadObjectFromStream(beforeRun) as CogToolBlock;
}

3.4 示例 4:批量配置文件管理器

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// VisionPro 配置文件管理器
/// 支持批量加载、保存、备份和版本管理
/// </summary>
public class VppConfigManager
{
    private readonly string _configDirectory;
    private readonly string _backupDirectory;

    public VppConfigManager(string configDirectory)
    {
        _configDirectory = configDirectory;
        _backupDirectory = Path.Combine(configDirectory, "Backups");
        
        if (!Directory.Exists(_backupDirectory))
            Directory.CreateDirectory(_backupDirectory);
    }

    /// <summary>
    /// 加载所有 .vpp 配置文件
    /// </summary>
    public Dictionary<string, CogToolBlock> LoadAllConfigs()
    {
        var configs = new Dictionary<string, CogToolBlock>();
        
        foreach (string file in Directory.GetFiles(_configDirectory, "*.vpp"))
        {
            try
            {
                string key = Path.GetFileNameWithoutExtension(file);
                CogToolBlock block = CogSerializer.LoadObjectFromFile(file) as CogToolBlock;
                if (block != null)
                {
                    configs[key] = block;
                    Console.WriteLine($"✓ 已加载: {key}");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"✗ 加载失败 {Path.GetFileName(file)}: {ex.Message}");
            }
        }
        
        return configs;
    }

    /// <summary>
    /// 保存配置并自动创建备份
    /// </summary>
    public void SaveWithBackup(string name, CogToolBlock block)
    {
        string filePath = Path.Combine(_configDirectory, $"{name}.vpp");
        
        // 如果已存在同名文件,先备份
        if (File.Exists(filePath))
        {
            string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss");
            string backupPath = Path.Combine(_backupDirectory, $"{name}_{timestamp}.vpp");
            File.Copy(filePath, backupPath, true);
            Console.WriteLine($"已备份至: {backupPath}");
        }
        
        // 保存新配置
        CogSerializer.SaveObjectToFile(block, filePath);
        Console.WriteLine($"已保存: {filePath}");
    }

    /// <summary>
    /// 导出精简版配置(排除绑定和图形,便于迁移)
    /// </summary>
    public void ExportPortable(string name, CogToolBlock block)
    {
        string exportPath = Path.Combine(_configDirectory, $"{name}_portable.vpp");
        CogSerializer.SaveObjectToFile(
            block, exportPath, null,
            CogSerializationOptionsConstants.ExcludeDataBindings
            | CogSerializationOptionsConstants.ExcludeGraphics
        );
        Console.WriteLine($"已导出精简版: {exportPath}");
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        var manager = new VppConfigManager(@"D:\VisionConfigs");
        
        // 批量加载
        var configs = manager.LoadAllConfigs();
        
        // 保存并自动备份
        if (configs.ContainsKey("Inspection"))
        {
            configs["Inspection"].Run();
            manager.SaveWithBackup("Inspection", configs["Inspection"]);
            manager.ExportPortable("Inspection", configs["Inspection"]);
        }
    }
}

3.5 示例 5:深度复制在A/B测试中的应用

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro.PMAlign;

/// <summary>
/// 使用深度复制实现参数 A/B 测试
/// 在不影响原始配置的情况下对比不同参数的效果
/// </summary>
public class ParameterABTest
{
    private CogToolBlock _originalBlock;

    public ParameterABTest(CogToolBlock block)
    {
        _originalBlock = block;
    }

    /// <summary>
    /// 对比两组参数的检测结果
    /// </summary>
    public void CompareParameters(
        Action<CogPMAlignTool> configA, 
        Action<CogPMAlignTool> configB,
        CogImage8Grey testImage)
    {
        // 深度复制两份独立的 ToolBlock
        CogToolBlock blockA = CogSerializer.DeepCopyObject(_originalBlock) as CogToolBlock;
        CogToolBlock blockB = CogSerializer.DeepCopyObject(_originalBlock) as CogToolBlock;

        // 分别应用不同参数
        CogPMAlignTool toolA = blockA.Tools["PMAlign1"] as CogPMAlignTool;
        CogPMAlignTool toolB = blockB.Tools["PMAlign1"] as CogPMAlignTool;

        configA?.Invoke(toolA);
        configB?.Invoke(toolB);

        // 设置输入图像
        blockA.Inputs["Image"].Value = testImage;
        blockB.Inputs["Image"].Value = testImage;

        // 运行并对比
        blockA.Run();
        blockB.Run();

        // 分析结果差异
        Console.WriteLine("=== 参数 A 结果 ===");
        AnalyzeResults(blockA);
        Console.WriteLine("=== 参数 B 结果 ===");
        AnalyzeResults(blockB);
    }

    private void AnalyzeResults(CogToolBlock block)
    {
        foreach (ICogTool tool in block.Tools)
        {
            if (tool.RunStatus != null)
            {
                Console.WriteLine($"  {tool.Name}: {tool.RunStatus.Result}");
            }
        }
    }
}

3.6 示例 6:CogAcqFifoTool 相机配置的保存与加载

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ImageFile;

// ===== 保存相机采集工具配置 =====
// 假设已在 QuickBuild 中配置好相机参数
CogAcqFifoTool acqTool = new CogAcqFifoTool();
// ... 配置相机参数(曝光、增益、触发模式等) ...

CogSerializer.SaveObjectToFile(
    acqTool, 
    @"D:\Cameras\Camera1.vpp",
    null,
    CogSerializationOptionsConstants.Minimum
);

// ===== 加载相机配置到编辑控件 =====
CogAcqFifoTool loadedAcqTool = 
    CogSerializer.LoadObjectFromFile(@"D:\Cameras\Camera1.vpp") as CogAcqFifoTool;

if (loadedAcqTool != null)
{
    // 绑定到编辑控件进行显示/修改
    cogAcqFifoEditV21.Subject = loadedAcqTool;
    
    // 或直接使用采集
    loadedAcqTool.Run();
    CogImage8Grey image = loadedAcqTool.OutputImage as CogImage8Grey;
}

3.7 示例 7:异常处理与文件校验

csharp 复制代码
using Cognex.VisionPro;
using System;
using System.IO;

public static class SafeVppLoader
{
    /// <summary>
    /// 安全加载 VPP 文件,包含完整的异常处理
    /// </summary>
    public static T LoadSafe<T>(string filePath) where T : class
    {
        // 1. 文件存在性检查
        if (!File.Exists(filePath))
        {
            Console.WriteLine($"错误: 文件不存在 - {filePath}");
            return null;
        }

        // 2. 文件大小检查
        FileInfo fi = new FileInfo(filePath);
        if (fi.Length == 0)
        {
            Console.WriteLine($"错误: 文件为空 - {filePath}");
            return null;
        }

        try
        {
            // 3. 加载并转换
            object obj = CogSerializer.LoadObjectFromFile(filePath);
            
            if (obj is T typed)
            {
                Console.WriteLine($"成功加载: {filePath} (类型: {typeof(T).Name})");
                return typed;
            }
            else
            {
                Console.WriteLine($"类型不匹配: 期望 {typeof(T).Name}, 实际 {obj?.GetType().Name ?? "null"}");
                return null;
            }
        }
        catch (UnauthorizedAccessException)
        {
            Console.WriteLine($"权限不足: {filePath}");
        }
        catch (IOException ex)
        {
            Console.WriteLine($"IO 错误: {ex.Message}");
        }
        catch (Exception ex)
        {
            Console.WriteLine($"加载失败: {ex.Message}");
        }

        return null;
    }

    /// <summary>
    /// 安全保存 VPP 文件
    /// </summary>
    public static bool SaveSafe<T>(T obj, string filePath) where T : class
    {
        try
        {
            // 确保目录存在
            string dir = Path.GetDirectoryName(filePath);
            if (!string.IsNullOrEmpty(dir) && !Directory.Exists(dir))
                Directory.CreateDirectory(dir);

            CogSerializer.SaveObjectToFile(obj, filePath);
            Console.WriteLine($"保存成功: {filePath}");
            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"保存失败: {ex.Message}");
            return false;
        }
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        // 安全加载
        var block = SafeVppLoader.LoadSafe<CogToolBlock>(@"D:\Configs\Block.vpp");
        if (block != null)
        {
            block.Run();
        }

        // 安全保存
        SafeVppLoader.SaveSafe(block, @"D:\Configs\Block_backup.vpp");
    }
}

4.CogSerializer补充示例集

4.1 ToolBlock 输入输出连线的序列化与恢复

在实际项目中,CogToolBlock 的输入/输出终端(Terminal)定义和工具间的数据连线是核心配置。序列化时需要确保连线信息被完整保留。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro.PMAlign;
using Cognex.VisionPro.Caliper;
using System;

/// <summary>
/// 创建带完整输入输出连线的 ToolBlock,并进行序列化
/// </summary>
public class ToolBlockWiringDemo
{
    /// <summary>
    /// 创建一个完整的检测 ToolBlock:
    ///   输入:Image (CogImage8Grey)
    ///   工具链:PMAlign定位 → Fixture校正 → Caliper测量
    ///   输出:ResultX, ResultY, Width, Pass
    /// </summary>
    public static CogToolBlock CreateInspectionBlock()
    {
        CogToolBlock block = new CogToolBlock();
        block.Name = "InspectionBlock";

        // ===== 1. 定义输入终端 =====
        block.Inputs.Add(new CogToolBlockTerminal("Image", typeof(CogImage8Grey)));

        // ===== 2. 添加工具 =====
        // 模板匹配工具(定位)
        CogPMAlignTool pmaTool = new CogPMAlignTool();
        pmaTool.Name = "PMAlign_Locate";
        block.Tools.Add(pmaTool);

        // 定位工具(坐标校正)
        CogFixtureTool fixtureTool = new CogFixtureTool();
        fixtureTool.Name = "Fixture_Correct";
        block.Tools.Add(fixtureTool);

        // 卡尺工具(宽度测量)
        CogCaliperTool caliperTool = new CogCaliperTool();
        caliperTool.Name = "Caliper_Width";
        block.Tools.Add(caliperTool);

        // ===== 3. 建立工具间数据连线 =====
        // Image → PMAlign 输入
        block.Connect(block.Inputs["Image"], pmaTool, "InputImage");

        // PMAlign 的输出图像 → Fixture 的输入图像
        block.Connect(pmaTool, "OutputImage", fixtureTool, "InputImage");

        // Fixture 的输出图像 → Caliper 的输入图像
        block.Connect(fixtureTool, "OutputImage", caliperTool, "InputImage");

        // ===== 4. 定义输出终端 =====
        // 从 PMAlign 获取定位坐标
        block.Outputs.Add(new CogToolBlockTerminal("ResultX",
            pmaTool, "Results.GetPose().TranslationX"));
        block.Outputs.Add(new CogToolBlockTerminal("ResultY",
            pmaTool, "Results.GetPose().TranslationY"));

        // 从 Caliper 获取宽度
        // 注意:实际项目中需要根据 Caliper 结果的具体路径来设置
        // block.Outputs.Add(new CogToolBlockTerminal("Width",
        //     caliperTool, "Results.Width"));

        return block;
    }

    /// <summary>
    /// 保存 ToolBlock 配置(保留所有连线信息)
    /// </summary>
    public static void SaveInspectionConfig(CogToolBlock block, string path)
    {
        // 使用 Minimum 选项即可保留连线信息
        // 连线属于工具间关系,不属于 DataBinding 或 Graphics
        CogSerializer.SaveObjectToFile(block, path);
        Console.WriteLine($"[保存] 工具数量: {block.Tools.Count}, 输入: {block.Inputs.Count}, 输出: {block.Outputs.Count}");
    }

    /// <summary>
    /// 加载并验证 ToolBlock 配置
    /// </summary>
    public static CogToolBlock LoadAndVerify(string path)
    {
        CogToolBlock block = CogSerializer.LoadObjectFromFile(path) as CogToolBlock;
        if (block == null)
        {
            Console.WriteLine("[错误] 加载失败");
            return null;
        }

        Console.WriteLine($"[加载成功] 名称: {block.Name}");
        Console.WriteLine($"  工具数量: {block.Tools.Count}");
        Console.WriteLine($"  输入终端: {block.Inputs.Count}");
        Console.WriteLine($"  输出终端: {block.Outputs.Count}");

        // 遍历所有工具检查状态
        foreach (ICogTool tool in block.Tools)
        {
            Console.WriteLine($"  工具: {tool.Name} ({tool.GetType().Name})");
        }

        return block;
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        string configPath = @"D:\Configs\InspectionWired.vpp";

        // 创建并保存
        CogToolBlock block = ToolBlockWiringDemo.CreateInspectionBlock();
        ToolBlockWiringDemo.SaveInspectionConfig(block, configPath);

        // 加载并验证连线
        CogToolBlock loaded = ToolBlockWiringDemo.LoadAndVerify(configPath);

        // 运行检测(需要先设置输入图像)
        // loaded.Inputs["Image"].Value = myImage;
        // loaded.Run();
    }
}

4.2 模板训练结果的持久化与复用

CogPMAlignTool 的 Pattern(模板)训练是一个耗时操作,通常在开发阶段完成训练后保存,运行阶段直接加载。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.PMAlign;
using System;
using System.IO;

/// <summary>
/// 模板管理器:支持训练、保存、加载、版本管理
/// </summary>
public class PatternManager
{
    private readonly string _patternDir;

    public PatternManager(string patternDirectory)
    {
        _patternDir = patternDirectory;
        if (!Directory.Exists(_patternDir))
            Directory.CreateDirectory(_patternDir);
    }

    /// <summary>
    /// 训练并保存模板
    /// </summary>
    public bool TrainAndSave(string patternName, CogImage8Grey trainImage,
        CogPMAlignPattern.PatternTypeConstants patternType = CogPMAlignPattern.PatternTypeConstants.PatMax)
    {
        try
        {
            CogPMAlignTool tool = new CogPMAlignTool();
            tool.Pattern.TrainImage = trainImage;
            tool.Pattern.TrainImageTransform = new CogTransform2DRigid();
            tool.Pattern.Origin.TranslationX = trainImage.Width / 2.0;
            tool.Pattern.Origin.TranslationY = trainImage.Height / 2.0;

            // 设置匹配类型
            tool.Pattern.PatternType = patternType;

            // 执行训练
            tool.Pattern.Train();

            if (!tool.Pattern.Trained)
            {
                Console.WriteLine($"[错误] 模板训练失败: {patternName}");
                return false;
            }

            // 保存整个工具(包含 Pattern)
            string toolPath = Path.Combine(_patternDir, $"{patternName}_tool.vpp");
            CogSerializer.SaveObjectToFile(tool, toolPath);

            // 也可以单独保存 Pattern
            string patternPath = Path.Combine(_patternDir, $"{patternName}_pattern.vpp");
            CogSerializer.SaveObjectToFile(tool.Pattern, patternPath);

            Console.WriteLine($"[成功] 模板已训练并保存: {patternName}");
            Console.WriteLine($"  特征点数: {tool.Pattern.NumTrainedPoints}");
            Console.WriteLine($"  工具文件: {toolPath}");
            Console.WriteLine($"  模板文件: {patternPath}");

            return true;
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[异常] {ex.Message}");
            return false;
        }
    }

    /// <summary>
    /// 加载已保存的模板到工具
    /// </summary>
    public CogPMAlignTool LoadPattern(string patternName)
    {
        string toolPath = Path.Combine(_patternDir, $"{patternName}_tool.vpp");

        if (!File.Exists(toolPath))
        {
            // 尝试单独加载 Pattern
            string patternPath = Path.Combine(_patternDir, $"{patternName}_pattern.vpp");
            if (File.Exists(patternPath))
            {
                CogPMAlignPattern pattern = CogSerializer.LoadObjectFromFile(patternPath) as CogPMAlignPattern;
                if (pattern != null)
                {
                    CogPMAlignTool tool = new CogPMAlignTool();
                    tool.Pattern = pattern;
                    Console.WriteLine($"[加载] 单独模板: {patternName}");
                    return tool;
                }
            }
            Console.WriteLine($"[未找到] 模板: {patternName}");
            return null;
        }

        CogPMAlignTool loadedTool = CogSerializer.LoadObjectFromFile(toolPath) as CogPMAlignTool;
        if (loadedTool != null)
        {
            Console.WriteLine($"[加载] 完整工具: {patternName}, 训练状态: {loadedTool.Pattern.Trained}");
        }

        return loadedTool;
    }

    /// <summary>
    /// 列出所有已保存的模板
    /// </summary>
    public void ListPatterns()
    {
        Console.WriteLine("=== 已保存的模板 ===");
        foreach (string file in Directory.GetFiles(_patternDir, "*_tool.vpp"))
        {
            string name = Path.GetFileNameWithoutExtension(file).Replace("_tool", "");
            FileInfo fi = new FileInfo(file);
            Console.WriteLine($"  {name} - {fi.LastWriteTime:yyyy-MM-dd HH:mm} - {fi.Length / 1024}KB");
        }
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        var mgr = new PatternManager(@"D:\Patterns");

        // 训练模板(首次)
        CogImage8Grey trainImage = LoadImage(); // 你的图像加载方法
        mgr.TrainAndSave("Connector_Pin", trainImage);

        // 后续直接加载
        CogPMAlignTool tool = mgr.LoadPattern("Connector_Pin");
        if (tool != null)
        {
            tool.InputImage = LoadRuntimeImage();
            tool.Run();

            if (tool.Results != null && tool.Results.Count > 0)
            {
                Console.WriteLine($"匹配成功: X={tool.Results[0].Pose.TranslationX:F2}, " +
                                  $"Y={tool.Results[0].Pose.TranslationY:F2}, " +
                                  $"Score={tool.Results[0].Score:F4}");
            }
        }
    }
}

4.3 Fixture定位工具的配置持久化

CogFixtureTool用于坐标空间变换,在多工位检测中非常常见。其配置(原点、角度、比例)需要持久化。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.CalibFix;
using Cognex.VisionPro.PMAlign;
using System;

/// <summary>
/// 定位工具配置管理
/// 支持保存/加载 Fixture 配置,以及基于 PMAlign 结果动态更新
/// </summary>
public class FixtureConfigManager
{
    /// <summary>
    /// 创建基于 PMAlign 结果的 Fixture 配置
    /// </summary>
    public static CogFixtureTool CreateFixtureFromPMAlign(
        CogPMAlignTool pmaTool, string fixtureName = "Fixture1")
    {
        CogFixtureTool fixture = new CogFixtureTool();
        fixture.Name = fixtureName;

        // 设置 Fixture 的工作模式
        fixture.RunParams.UnfixturedImageFixturedImageSpaceNameChange =
            CogFixtureToolFixedImageSpaceNameChangeConstants.FixedImageName;

        // 如果 PMAlign 已运行且有结果,基于其位姿设置 Fixture
        if (pmaTool.Results != null && pmaTool.Results.Count > 0)
        {
            fixture.RunParams.FixturedTransformName = "Fixture";
            fixture.RunParams.TrainImage = pmaTool.Results[0].GetPose();

            // 使用 PMAlign 的输出图像作为 Fixture 输入
            fixture.InputImage = pmaTool.OutputImage;
        }

        return fixture;
    }

    /// <summary>
    /// 保存 Fixture 配置
    /// </summary>
    public static void SaveFixture(CogFixtureTool fixture, string path)
    {
        CogSerializer.SaveObjectToFile(fixture, path);
        Console.WriteLine($"[保存] Fixture: {fixture.Name}");
        Console.WriteLine($"  变换名称: {fixture.RunParams.FixturedTransformName}");
    }

    /// <summary>
    /// 加载 Fixture 配置
    /// </summary>
    public static CogFixtureTool LoadFixture(string path)
    {
        CogFixtureTool fixture = CogSerializer.LoadObjectFromFile(path) as CogFixtureTool;
        if (fixture != null)
        {
            Console.WriteLine($"[加载] Fixture: {fixture.Name}");
            Console.WriteLine($"  变换名称: {fixture.RunParams.FixturedTransformName}");
        }
        return fixture;
    }

    /// <summary>
    /// 保存完整的定位+检测流程
    /// </summary>
    public static void SaveFixtureWorkflow(
        CogPMAlignTool pmaTool, CogFixtureTool fixture, string dir)
    {
        // 分别保存各工具
        CogSerializer.SaveObjectToFile(pmaTool, System.IO.Path.Combine(dir, "PMAlign.vpp"));
        CogSerializer.SaveObjectToFile(fixture, System.IO.Path.Combine(dir, "Fixture.vpp"));

        // 也可以用 ToolBlock 打包保存
        CogToolBlock block = new CogToolBlock();
        block.Name = "FixtureWorkflow";
        block.Tools.Add(CogSerializer.DeepCopyObject(pmaTool));
        block.Tools.Add(CogSerializer.DeepCopyObject(fixture));
        CogSerializer.SaveObjectToFile(block, System.IO.Path.Combine(dir, "Workflow.vpp"));

        Console.WriteLine("[保存] 定位流程已保存(单独文件 + 整合包)");
    }
}

4.4 多相机配置管理

在多相机系统中,每台相机的参数(曝光、增益、触发模式)不同,需要独立保存和管理。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System;
using System.Collections.Generic;
using System.IO;

/// <summary>
/// 多相机配置管理器
/// </summary>
public class MultiCameraConfigManager
{
    private readonly string _configRoot;
    private readonly Dictionary<string, CogAcqFifoTool> _cameras = new Dictionary<string, CogAcqFifoTool>();

    public MultiCameraConfigManager(string configRoot)
    {
        _configRoot = configRoot;
    }

    /// <summary>
    /// 保存单台相机配置
    /// </summary>
    public void SaveCameraConfig(string cameraName, CogAcqFifoTool acqTool)
    {
        string cameraDir = Path.Combine(_configRoot, cameraName);
        if (!Directory.Exists(cameraDir))
            Directory.CreateDirectory(cameraDir);

        string path = Path.Combine(cameraDir, "Camera.vpp");
        CogSerializer.SaveObjectToFile(acqTool, path);

        // 同时保存一份备份
        string backupDir = Path.Combine(cameraDir, "Backup");
        if (!Directory.Exists(backupDir))
            Directory.CreateDirectory(backupDir);
        string backupPath = Path.Combine(backupDir, $"Camera_{DateTime.Now:yyyyMMdd_HHmmss}.vpp");
        File.Copy(path, backupPath, true);

        _cameras[cameraName] = acqTool;
        Console.WriteLine($"[保存] 相机 {cameraName} 配置已保存");
    }

    /// <summary>
    /// 加载单台相机配置
    /// </summary>
    public CogAcqFifoTool LoadCameraConfig(string cameraName)
    {
        string path = Path.Combine(_configRoot, cameraName, "Camera.vpp");
        if (!File.Exists(path))
        {
            Console.WriteLine($"[未找到] 相机 {cameraName} 配置文件");
            return null;
        }

        CogAcqFifoTool acqTool = CogSerializer.LoadObjectFromFile(path) as CogAcqFifoTool;
        if (acqTool != null)
        {
            _cameras[cameraName] = acqTool;
            Console.WriteLine($"[加载] 相机 {cameraName} 配置已加载");
        }
        return acqTool;
    }

    /// <summary>
    /// 加载所有相机配置
    /// </summary>
    public void LoadAllCameras()
    {
        if (!Directory.Exists(_configRoot))
        {
            Console.WriteLine("[错误] 配置目录不存在");
            return;
        }

        foreach (string cameraDir in Directory.GetDirectories(_configRoot))
        {
            string cameraName = Path.GetFileName(cameraDir);
            if (cameraName == "Backup") continue;
            LoadCameraConfig(cameraName);
        }

        Console.WriteLine($"[完成] 共加载 {_cameras.Count} 台相机配置");
    }

    /// <summary>
    /// 创建多相机 ToolBlock(含所有相机的采集工具)
    /// </summary>
    public CogToolBlock CreateMultiCameraBlock()
    {
        CogToolBlock block = new CogToolBlock();
        block.Name = "MultiCamera";

        foreach (var kvp in _cameras)
        {
            // 深度复制,避免修改原始配置
            CogAcqFifoTool copy = CogSerializer.DeepCopyObject(kvp.Value) as CogAcqFifoTool;
            copy.Name = $"Acq_{kvp.Key}";
            block.Tools.Add(copy);

            // 添加输出终端
            block.Outputs.Add(new CogToolBlockTerminal($"{kvp.Key}_Image",
                copy, "OutputImage"));
        }

        return block;
    }

    /// <summary>
    /// 列出所有相机
    /// </summary>
    public void ListCameras()
    {
        Console.WriteLine("=== 相机列表 ===");
        foreach (var kvp in _cameras)
        {
            Console.WriteLine($"  {kvp.Key}: {kvp.Value.GetType().Name}");
        }
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        var mgr = new MultiCameraConfigManager(@"D:\Cameras");

        // 首次配置并保存
        CogAcqFifoTool cam1 = new CogAcqFifoTool();
        // ... 配置相机1参数 ...
        mgr.SaveCameraConfig("TopCamera", cam1);

        CogAcqFifoTool cam2 = new CogAcqFifoTool();
        // ... 配置相机2参数 ...
        mgr.SaveCameraConfig("SideCamera", cam2);

        // 后续直接加载
        mgr.LoadAllCameras();
        mgr.ListCameras();

        // 创建多相机 ToolBlock
        CogToolBlock multiCam = mgr.CreateMultiCameraBlock();
        CogSerializer.SaveObjectToFile(multiCam, @"D:\Configs\MultiCamera.vpp");
    }
}

4.5 ToolBlock运行结果的序列化与分析

将ToolBlock的运行结果(包括状态、输出值、图形标注)保存下来,用于离线分析和质量追溯。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System;
using System.IO;

/// <summary>
/// 运行结果快照:保存 ToolBlock 运行前后的完整状态
/// </summary>
public class RunResultSnapshot
{
    public string Timestamp { get; set; }
    public string BlockName { get; set; }
    public CogToolResultConstants OverallResult { get; set; }
    public CogToolBlock BlockSnapshot { get; set; }  // 运行后的 Block 副本

    public override string ToString()
    {
        return $"[{Timestamp}] {BlockName} => {OverallResult}";
    }
}

/// <summary>
/// 运行结果管理器:支持结果保存、加载、批量分析
/// </summary>
public class RunResultManager
{
    private readonly string _resultDir;
    private int _sequence = 0;

    public RunResultManager(string resultDirectory)
    {
        _resultDir = resultDirectory;
        if (!Directory.Exists(_resultDir))
            Directory.CreateDirectory(_resultDir);
    }

    /// <summary>
    /// 运行 ToolBlock 并保存结果快照
    /// </summary>
    public RunResultSnapshot RunAndCapture(CogToolBlock block, object inputImage = null)
    {
        // 设置输入
        if (inputImage != null && block.Inputs.Count > 0)
        {
            block.Inputs[0].Value = inputImage;
        }

        // 运行前快照(使用流)
        MemoryStream beforeSnapshot = new MemoryStream();
        CogSerializer.SaveObjectToStream(block, beforeSnapshot);

        // 执行运行
        block.Run();

        // 运行后深度复制
        CogToolBlock afterSnapshot = CogSerializer.DeepCopyObject(block) as CogToolBlock;

        _sequence++;
        string timestamp = DateTime.Now.ToString("yyyyMMdd_HHmmss_fff");

        var result = new RunResultSnapshot
        {
            Timestamp = timestamp,
            BlockName = block.Name,
            OverallResult = block.RunStatus.Result,
            BlockSnapshot = afterSnapshot
        };

        // 保存到文件
        string filePath = Path.Combine(_resultDir, $"Run_{_sequence:D6}_{timestamp}.vpp");
        CogSerializer.SaveObjectToFile(afterSnapshot, filePath);

        Console.WriteLine($"[运行 #{_sequence}] {result}");

        // 清理
        beforeSnapshot.Dispose();

        return result;
    }

    /// <summary>
    /// 加载历史运行结果
    /// </summary>
    public RunResultSnapshot LoadResult(string filePath)
    {
        CogToolBlock block = CogSerializer.LoadObjectFromFile(filePath) as CogToolBlock;
        if (block == null) return null;

        return new RunResultSnapshot
        {
            Timestamp = File.GetLastWriteTime(filePath).ToString("yyyy-MM-dd HH:mm:ss"),
            BlockName = block.Name,
            OverallResult = block.RunStatus?.Result ?? CogToolResultConstants.Error,
            BlockSnapshot = block
        };
    }

    /// <summary>
    /// 批量加载并统计运行结果
    /// </summary>
    public void AnalyzeHistory()
    {
        var files = Directory.GetFiles(_resultDir, "Run_*.vpp");
        int total = 0, pass = 0, fail = 0, error = 0;

        Console.WriteLine($"=== 历史运行分析 ({files.Length} 条记录) ===");

        foreach (string file in files)
        {
            try
            {
                var result = LoadResult(file);
                if (result == null) continue;

                total++;
                switch (result.OverallResult)
                {
                    case CogToolResultConstants.Accept: pass++; break;
                    case CogToolResultConstants.Reject: fail++; break;
                    default: error++; break;
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"  加载失败 {Path.GetFileName(file)}: {ex.Message}");
                error++;
            }
        }

        Console.WriteLine($"  总计: {total}, 通过: {pass}, 失败: {fail}, 异常: {error}");
        Console.WriteLine($"  通过率: {(total > 0 ? (double)pass / total * 100 : 0):F1}%");
    }

    /// <summary>
    /// 清理旧结果(保留最近 N 条)
    /// </summary>
    public void Cleanup(int keepCount = 1000)
    {
        var files = Directory.GetFiles(_resultDir, "Run_*.vpp");
        if (files.Length <= keepCount) return;

        Array.Sort(files, (a, b) => File.GetLastWriteTime(a).CompareTo(File.GetLastWriteTime(b)));

        int deleteCount = files.Length - keepCount;
        for (int i = 0; i < deleteCount; i++)
        {
            File.Delete(files[i]);
        }
        Console.WriteLine($"[清理] 删除 {deleteCount} 条旧记录,保留 {keepCount} 条");
    }
}

4.6 标定数据的保存与恢复

相机标定(CogCalibNPointToNPointToolCogCalibCheckerboardTool)的配置需要持久化,以便在系统重启后快速恢复。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.CalibFix;
using System;

/// <summary>
/// 标定数据管理:保存和恢复标定配置
/// </summary>
public class CalibrationManager
{
    /// <summary>
    /// 保存 N 点标定工具配置
    /// </summary>
    public static void SaveNPointCalibration(
        CogCalibNPointToNPointTool calibTool, string path)
    {
        CogSerializer.SaveObjectToFile(calibTool, path);
        Console.WriteLine($"[保存] N点标定: {path}");
        Console.WriteLine($"  已标定: {calibTool.Calibration.NumPoints} 点");
    }

    /// <summary>
    /// 加载 N 点标定工具配置
    /// </summary>
    public static CogCalibNPointToNPointTool LoadNPointCalibration(string path)
    {
        CogCalibNPointToNPointTool calibTool =
            CogSerializer.LoadObjectFromFile(path) as CogCalibNPointToNPointTool;

        if (calibTool != null)
        {
            Console.WriteLine($"[加载] N点标定: {path}");
            Console.WriteLine($"  已标定: {calibTool.Calibration.NumPoints} 点");
        }
        return calibTool;
    }

    /// <summary>
    /// 保存棋盘格标定工具配置
    /// </summary>
    public static void SaveCheckerboardCalibration(
        CogCalibCheckerboardTool calibTool, string path)
    {
        CogSerializer.SaveObjectToFile(calibTool, path);
        Console.WriteLine($"[保存] 棋盘格标定: {path}");
    }

    /// <summary>
    /// 加载棋盘格标定工具配置
    /// </summary>
    public static CogCalibCheckerboardTool LoadCheckerboardCalibration(string path)
    {
        CogCalibCheckerboardTool calibTool =
            CogSerializer.LoadObjectFromFile(path) as CogCalibCheckerboardTool;
        if (calibTool != null)
        {
            Console.WriteLine($"[加载] 棋盘格标定: {path}");
        }
        return calibTool;
    }

    /// <summary>
    /// 创建包含标定的完整检测流程
    /// </summary>
    public static CogToolBlock CreateCalibratedInspection(
        CogCalibNPointToNPointTool calibTool, CogToolBlock inspectionBlock)
    {
        CogToolBlock calibratedBlock = new CogToolBlock();
        calibratedBlock.Name = "CalibratedInspection";

        // 添加标定工具
        calibratedBlock.Tools.Add(CogSerializer.DeepCopyObject(calibTool));

        // 添加检测工具
        calibratedBlock.Tools.Add(CogSerializer.DeepCopyObject(inspectionBlock));

        // 连线:标定输出图像 → 检测输入图像
        // (实际连线需根据具体工具接口设置)

        return calibratedBlock;
    }
}

4.7 配置热更新(运行时动态替换)

在不停机的情况下,动态加载新的检测配置并替换当前运行的 ToolBlock。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System;
using System.IO;
using System.Threading;

/// <summary>
/// 配置热更新管理器
/// 支持文件监控和运行时配置替换
/// </summary>
public class HotReloadManager : IDisposable
{
    private CogToolBlock _activeBlock;
    private readonly string _configPath;
    private FileSystemWatcher _watcher;
    private readonly object _lock = new object();
    private DateTime _lastLoadTime = DateTime.MinValue;

    public event Action<string> OnConfigReloaded;

    public HotReloadManager(string configPath)
    {
        _configPath = configPath;

        // 加载初始配置
        ReloadConfig();

        // 设置文件监控
        string dir = Path.GetDirectoryName(configPath);
        string fileName = Path.GetFileName(configPath);

        _watcher = new FileSystemWatcher(dir, fileName);
        _watcher.Changed += OnFileChanged;
        _watcher.EnableRaisingEvents = true;

        Console.WriteLine($"[热更新] 已启动监控: {configPath}");
    }

    /// <summary>
    /// 获取当前活跃的 ToolBlock(线程安全)
    /// </summary>
    public CogToolBlock ActiveBlock
    {
        get
        {
            lock (_lock)
            {
                return _activeBlock;
            }
        }
    }

    /// <summary>
    /// 运行当前配置
    /// </summary>
    public void Run(object inputImage = null)
    {
        lock (_lock)
        {
            if (_activeBlock == null)
            {
                Console.WriteLine("[错误] 无可用配置");
                return;
            }

            if (inputImage != null && _activeBlock.Inputs.Count > 0)
            {
                _activeBlock.Inputs[0].Value = inputImage;
            }

            _activeBlock.Run();
        }
    }

    private void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        // 延迟加载,避免文件写入未完成
        Thread.Sleep(500);
        ReloadConfig();
    }

    private void ReloadConfig()
    {
        try
        {
            // 防止重复加载
            if (File.Exists(_configPath))
            {
                DateTime lastWrite = File.GetLastWriteTime(_configPath);
                if (lastWrite <= _lastLoadTime) return;
                _lastLoadTime = lastWrite;
            }

            CogToolBlock newBlock = CogSerializer.LoadObjectFromFile(_configPath) as CogToolBlock;
            if (newBlock == null)
            {
                Console.WriteLine("[警告] 配置加载失败");
                return;
            }

            lock (_lock)
            {
                _activeBlock = newBlock;
            }

            Console.WriteLine($"[热更新] 配置已重新加载: {newBlock.Name}, 工具数: {newBlock.Tools.Count}");
            OnConfigReloaded?.Invoke(_configPath);
        }
        catch (Exception ex)
        {
            Console.WriteLine($"[热更新错误] {ex.Message}");
        }
    }

    public void Dispose()
    {
        _watcher?.Dispose();
    }
}

// 使用示例
class Program
{
    static void Main()
    {
        string configPath = @"D:\Configs\LiveInspection.vpp";

        using (var hotReload = new HotReloadManager(configPath))
        {
            hotReload.OnConfigReloaded += path =>
            {
                Console.WriteLine($"  => 配置已更新: {path}");
            };

            // 模拟持续运行
            while (true)
            {
                hotReload.Run(); // 始终使用最新配置
                Thread.Sleep(100);
            }
        }
    }
}

4.8 ToolBlock高级脚本中的序列化应用

在ToolBlock脚本中使用CogSerializer实现动态配置切换和结果保存。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro.Caliper;
using Cognex.VisionPro.Dimensioning;
using System;
using System.Drawing;

/// <summary>
/// ToolBlock 高级脚本示例:
/// 在脚本中实现配置动态加载和结果图形标注
/// 
/// 继承自 CogToolBlockAdvancedScriptBase
/// </summary>
public class AdvancedScriptDemo : CogToolBlockAdvancedScriptBase
{
    private CogToolBlock mToolBlock;

    // 工具引用
    private CogFindCircleTool[] findCircle = new CogFindCircleTool[2];
    private CogDistanceCircleCircleTool distanceTool;

    // 结果显示
    private CogGraphicLabel[] labels = new CogGraphicLabel[4];

    /// <summary>
    /// 初始化:获取工具引用
    /// </summary>
    public override void Initialize(CogToolGroup host)
    {
        base.Initialize(host);
        mToolBlock = (CogToolBlock)host;

        // 获取工具引用
        findCircle[0] = mToolBlock.Tools["CogFindCircleTool1"] as CogFindCircleTool;
        findCircle[1] = mToolBlock.Tools["CogFindCircleTool2"] as CogFindCircleTool;
        distanceTool = mToolBlock.Tools["CogDistanceCircleCircleTool1"] as CogDistanceCircleCircleTool;
    }

    /// <summary>
    /// 运行所有工具并分析结果
    /// </summary>
    public override bool GroupRun(ref string message, ref CogToolResultConstants result)
    {
        // 依次运行所有工具
        foreach (ICogTool tool in mToolBlock.Tools)
        {
            mToolBlock.RunTool(tool, ref message, ref result);
        }

        // 获取结果
        double radius0 = Math.Round(findCircle[0].Results.GetCircle().Radius, 2);
        double radius1 = Math.Round(findCircle[1].Results.GetCircle().Radius, 2);
        double distance = Math.Round(distanceTool.Distance, 2);

        // 判断合格/不合格
        bool isPass = (distance >= 129.5 && distance <= 130.5);

        // 创建结果标签
        labels[0] = CreateLabel(findCircle[0].Results.GetCircle().CenterX,
            findCircle[0].Results.GetCircle().CenterY + 30,
            radius0.ToString(), Color.Green);

        labels[1] = CreateLabel(findCircle[1].Results.GetCircle().CenterX,
            findCircle[1].Results.GetCircle().CenterY + 30,
            radius1.ToString(), Color.Green);

        labels[2] = CreateLabel(0, -20, distance.ToString(),
            isPass ? Color.Green : Color.Red);

        labels[3] = CreateLabel(0, -70, isPass ? "OK" : "NG",
            isPass ? Color.Green : Color.Red);

        // 设置整体结果
        result = isPass ? CogToolResultConstants.Accept : CogToolResultConstants.Reject;

        return false; // 返回 false,由用户控制后续流程
    }

    /// <summary>
    /// 在运行记录中添加图形标注
    /// </summary>
    public override void ModifyLastRunRecord(ICogRecord lastRecord)
    {
        string recordPath = "CogFixtureTool1.OutputImage";

        foreach (var label in labels)
        {
            if (label != null)
            {
                mToolBlock.AddGraphicToRunRecord(label, lastRecord, recordPath, "Script");
            }
        }
    }

    /// <summary>
    /// 辅助方法:创建图形标签
    /// </summary>
    private CogGraphicLabel CreateLabel(double x, double y, string text, Color color)
    {
        CogGraphicLabel label = new CogGraphicLabel();
        label.Font = new Font("Arial", 12);
        label.Color = color == Color.Green ? CogColorConstants.Green : CogColorConstants.Red;
        label.SetXYText(x, y, text);
        return label;
    }
}

4.9 配置版本管理与差异对比

实现配置文件的版本控制,支持版本回退和配置差异对比。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

/// <summary>
/// 配置版本管理器
/// </summary>
public class ConfigVersionManager
{
    private readonly string _configDir;
    private readonly string _versionDir;

    public ConfigVersionManager(string configDirectory)
    {
        _configDir = configDirectory;
        _versionDir = Path.Combine(configDirectory, ".versions");

        if (!Directory.Exists(_versionDir))
            Directory.CreateDirectory(_versionDir);
    }

    /// <summary>
    /// 保存配置并创建版本记录
    /// </summary>
    public void SaveWithVersion(string configName, CogToolBlock block, string commitMessage = "")
    {
        string configPath = Path.Combine(_configDir, $"{configName}.vpp");

        // 读取当前版本号
        int version = GetLatestVersion(configName) + 1;

        // 保存主配置
        CogSerializer.SaveObjectToFile(block, configPath);

        // 保存版本快照
        string versionPath = Path.Combine(_versionDir, $"{configName}_v{version:D4}.vpp");
        CogSerializer.SaveObjectToFile(block, versionPath);

        // 记录版本信息
        string infoPath = Path.Combine(_versionDir, $"{configName}_v{version:D4}.txt");
        File.WriteAllText(infoPath, $"版本: {version}\n" +
            $"时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\n" +
            $"说明: {commitMessage}\n" +
            $"工具数: {block.Tools.Count}\n");

        Console.WriteLine($"[版本 v{version}] {configName} - {commitMessage}");
    }

    /// <summary>
    /// 获取最新版本号
    /// </summary>
    private int GetLatestVersion(string configName)
    {
        var files = Directory.GetFiles(_versionDir, $"{configName}_v????.vpp");
        if (files.Length == 0) return 0;

        return files.Select(f =>
        {
            string name = Path.GetFileNameWithoutExtension(f);
            string verStr = name.Split('_').Last().TrimStart('v');
            return int.TryParse(verStr, out int v) ? v : 0;
        }).Max();
    }

    /// <summary>
    /// 回退到指定版本
    /// </summary>
    public CogToolBlock RevertToVersion(string configName, int version)
    {
        string versionPath = Path.Combine(_versionDir, $"{configName}_v{version:D4}.vpp");
        if (!File.Exists(versionPath))
        {
            Console.WriteLine($"[错误] 版本 v{version} 不存在");
            return null;
        }

        CogToolBlock block = CogSerializer.LoadObjectFromFile(versionPath) as CogToolBlock;
        if (block != null)
        {
            // 覆盖主配置
            string configPath = Path.Combine(_configDir, $"{configName}.vpp");
            CogSerializer.SaveObjectToFile(block, configPath);
            Console.WriteLine($"[回退] {configName} 已回退到 v{version}");
        }
        return block;
    }

    /// <summary>
    /// 列出所有版本
    /// </summary>
    public void ListVersions(string configName)
    {
        Console.WriteLine($"=== {configName} 版本历史 ===");

        var files = Directory.GetFiles(_versionDir, $"{configName}_v????.vpp")
            .OrderBy(f => f).ToArray();

        foreach (string file in files)
        {
            string name = Path.GetFileNameWithoutExtension(file);
            string verStr = name.Split('_').Last();
            string infoPath = Path.ChangeExtension(file, ".txt");
            string info = File.Exists(infoPath) ? File.ReadAllText(infoPath).Split('\n').Skip(3).FirstOrDefault() : "";

            FileInfo fi = new FileInfo(file);
            Console.WriteLine($"  {verStr} | {fi.LastWriteTime:yyyy-MM-dd HH:mm} | {fi.Length / 1024}KB | {info}");
        }
    }

    /// <summary>
    /// 对比两个版本的工具差异
    /// </summary>
    public void DiffVersions(string configName, int versionA, int versionB)
    {
        string pathA = Path.Combine(_versionDir, $"{configName}_v{versionA:D4}.vpp");
        string pathB = Path.Combine(_versionDir, $"{configName}_v{versionB:D4}.vpp");

        CogToolBlock blockA = CogSerializer.LoadObjectFromFile(pathA) as CogToolBlock;
        CogToolBlock blockB = CogSerializer.LoadObjectFromFile(pathB) as CogToolBlock;

        if (blockA == null || blockB == null)
        {
            Console.WriteLine("[错误] 无法加载版本进行对比");
            return;
        }

        Console.WriteLine($"=== 版本对比: v{versionA} vs v{versionB} ===");

        // 对比工具列表
        var toolsA = blockA.Tools.Cast<ICogTool>().Select(t => t.Name).ToHashSet();
        var toolsB = blockB.Tools.Cast<ICogTool>().Select(t => t.Name).ToHashSet();

        var added = toolsB.Except(toolsA);
        var removed = toolsA.Except(toolsB);
        var common = toolsA.Intersect(toolsB);

        if (added.Any()) Console.WriteLine($"  新增工具: {string.Join(", ", added)}");
        if (removed.Any()) Console.WriteLine($"  移除工具: {string.Join(", ", removed)}");
        Console.WriteLine($"  共有工具: {common.Count()}");

        // 对比输入输出
        Console.WriteLine($"  输入终端: v{versionA}={blockA.Inputs.Count}, v{versionB}={blockB.Inputs.Count}");
        Console.WriteLine($"  输出终端: v{versionA}={blockA.Outputs.Count}, v{versionB}={blockB.Outputs.Count}");
    }

    /// <summary>
    /// 清理旧版本(保留最近 N 个版本)
    /// </summary>
    public void CleanupVersions(string configName, int keepCount = 20)
    {
        var files = Directory.GetFiles(_versionDir, $"{configName}_v????.vpp")
            .OrderBy(f => f).ToArray();

        if (files.Length <= keepCount) return;

        int deleteCount = files.Length - keepCount;
        for (int i = 0; i < deleteCount; i++)
        {
            File.Delete(files[i]);
            string infoPath = Path.ChangeExtension(files[i], ".txt");
            if (File.Exists(infoPath)) File.Delete(infoPath);
        }

        Console.WriteLine($"[清理] 删除 {deleteCount} 个旧版本,保留最近 {keepCount} 个");
    }
}

4.10 完整的视觉检测流水线

将以上所有Demo整合为一个完整的视觉检测流水线,展示 CogSerializer在真实项目中的全流程应用。

csharp 复制代码
using Cognex.VisionPro;
using Cognex.VisionPro.ToolBlock;
using Cognex.VisionPro.PMAlign;
using Cognex.VisionPro.Caliper;
using Cognex.VisionPro.CalibFix;
using Cognex.VisionPro.Blob;
using Cognex.VisionPro.ImageFile;
using System;
using System.IO;

/// <summary>
/// 完整视觉检测流水线
/// 整合:标定、定位、测量、检测、结果管理
/// </summary>
public class VisionPipeline
{
    private readonly string _rootDir;
    private CogToolBlock _pipeline;
    private ConfigVersionManager _versionMgr;
    private RunResultManager _resultMgr;

    public VisionPipeline(string rootDir)
    {
        _rootDir = rootDir;
        _versionMgr = new ConfigVersionManager(Path.Combine(rootDir, "Configs"));
        _resultMgr = new RunResultManager(Path.Combine(rootDir, "Results"));
    }

    /// <summary>
    /// 初始化流水线(首次创建配置)
    /// </summary>
    public void Initialize()
    {
        _pipeline = new CogToolBlock();
        _pipeline.Name = "MainPipeline";

        // 1. 定义输入
        _pipeline.Inputs.Add(new CogToolBlockTerminal("Image", typeof(CogImage8Grey)));

        // 2. 添加定位工具
        CogPMAlignTool locator = new CogPMAlignTool();
        locator.Name = "Locator";
        _pipeline.Tools.Add(locator);

        // 3. 添加定位工具
        CogFixtureTool fixture = new CogFixtureTool();
        fixture.Name = "Fixture";
        _pipeline.Tools.Add(fixture);

        // 4. 添加测量工具
        CogCaliperTool caliper = new CogCaliperTool();
        caliper.Name = "WidthMeasure";
        _pipeline.Tools.Add(caliper);

        // 5. 添加检测工具
        CogBlobTool blob = new CogBlobTool();
        blob.Name = "DefectCheck";
        _pipeline.Tools.Add(blob);

        // 6. 建立连线
        // (简化示例,实际需根据具体接口设置)

        // 7. 定义输出
        _pipeline.Outputs.Add(new CogToolBlockTerminal("Pass", typeof(bool)));

        // 保存初始版本
        _versionMgr.SaveWithVersion("Pipeline", _pipeline, "初始版本");

        Console.WriteLine("[初始化] 流水线已创建");
    }

    /// <summary>
    /// 加载已有配置
    /// </summary>
    public bool Load()
    {
        string configPath = Path.Combine(_rootDir, "Configs", "Pipeline.vpp");
        if (!File.Exists(configPath))
        {
            Console.WriteLine("[提示] 配置不存在,将初始化新配置");
            Initialize();
            return true;
        }

        _pipeline = CogSerializer.LoadObjectFromFile(configPath) as CogToolBlock;
        if (_pipeline == null)
        {
            Console.WriteLine("[错误] 配置加载失败");
            return false;
        }

        Console.WriteLine($"[加载] 流水线: {_pipeline.Name}, 工具数: {_pipeline.Tools.Count}");
        return true;
    }

    /// <summary>
    /// 运行检测
    /// </summary>
    public bool Inspect(CogImage8Grey image)
    {
        if (_pipeline == null)
        {
            Console.WriteLine("[错误] 流水线未初始化");
            return false;
        }

        // 运行并捕获结果
        var snapshot = _resultMgr.RunAndCapture(_pipeline, image);

        bool pass = snapshot.OverallResult == CogToolResultConstants.Accept;
        Console.WriteLine($"[检测] 结果: {(pass ? "PASS" : "FAIL")}");

        return pass;
    }

    /// <summary>
    /// 更新配置(带版本管理)
    /// </summary>
    public void UpdateConfig(CogToolBlock newConfig, string reason)
    {
        _pipeline = newConfig;
        _versionMgr.SaveWithVersion("Pipeline", _pipeline, reason);
        Console.WriteLine($"[更新] 配置已更新: {reason}");
    }

    /// <summary>
    /// 回退配置
    /// </summary>
    public void RevertConfig(int version)
    {
        _pipeline = _versionMgr.RevertToVersion("Pipeline", version);
    }

    /// <summary>
    /// 导出配置(用于部署到其他设备)
    /// </summary>
    public void ExportForDeployment(string exportPath)
    {
        CogSerializer.SaveObjectToFile(_pipeline, exportPath, null,
            CogSerializationOptionsConstants.ExcludeDataBindings);
        Console.WriteLine($"[导出] 配置已导出: {exportPath}");
    }

    /// <summary>
    /// 查看历史
    /// </summary>
    public void ShowHistory()
    {
        _versionMgr.ListVersions("Pipeline");
        Console.WriteLine();
        _resultMgr.AnalyzeHistory();
    }

    /// <summary>
    /// 清理旧数据
    /// </summary>
    public void Cleanup()
    {
        _versionMgr.CleanupVersions("Pipeline", keepCount: 20);
        _resultMgr.Cleanup(keepCount: 1000);
    }
}

// ===== 主程序 =====
class Program
{
    static void Main()
    {
        var pipeline = new VisionPipeline(@"D:\VisionSystem");

        // 加载或初始化
        if (!pipeline.Load())
        {
            pipeline.Initialize();
        }

        // 模拟检测
        CogImage8Grey testImage = new CogImage8Grey(640, 480);
        for (int i = 0; i < 10; i++)
        {
            pipeline.Inspect(testImage);
        }

        // 查看历史
        pipeline.ShowHistory();

        // 导出部署
        pipeline.ExportForDeployment(@"D:\Export\Pipeline_deploy.vpp");

        // 清理
        pipeline.Cleanup();
    }
}

5.相关类速查表

5.1 核心序列化相关类

类名 命名空间 说明
CogSerializer Cognex.VisionPro 序列化主类
CogSerializationOptionsConstants Cognex.VisionPro 序列化选项枚举
CogSerializationOptionsAttribute Cognex.VisionPro 标记字段的可选序列化属性
CogSerializationDataBindingAttribute Cognex.VisionPro 标记数据绑定字段的序列化属性
CogSerializationBinderAttribute Cognex.VisionPro 程序集绑定策略属性

5.2 常用可序列化对象

对象类型 说明 典型用途
CogToolBlock 工具块,可包含多个工具及连线 封装完整的检测流程
CogToolGroup 工具组,逻辑集合 组织相关工具
CogPMAlignTool 模板匹配工具 定位、匹配
CogPMAlignPattern 模板匹配的 Pattern 对象 模板保存与复用
CogCaliperTool 卡尺工具 边缘检测、宽度测量
CogBlobTool 斑点工具 面积、数量统计
CogAcqFifoTool 相机采集工具 图像获取
CogImageFileTool 图像文件工具 图像读取/保存
CogFindLineTool 找线工具 直线检测
CogFindCircleTool 找圆工具 圆形检测
CogFixtureTool 定位工具 坐标空间变换
CogCalibNPointToNPointTool N点标定工具 相机标定

5.3 VisionPro工具与命名空间对照

工具名称 命名空间
CogAcqFifoTool Cognex.VisionPro.CogAcqFifoTool
CogBlobTool Cognex.VisionPro.Blob
CogCaliperTool Cognex.VisionPro.Caliper
CogCNLSearchTool Cognex.VisionPro.CNLSearch
CogImageFileTool Cognex.VisionPro.ImageFile
CogPMAlignTool Cognex.VisionPro.PMAlign
CogSearchMaxTool Cognex.VisionPro.SearchMax
CogToolBlock Cognex.VisionPro.ToolBlock
CogToolGroup Cognex.VisionPro.ToolGroup
CogCalibCheckerboardTool Cognex.VisionPro.CalibFix
CogFixtureTool Cognex.VisionPro.CalibFix
CogColorExtractorTool Cognex.VisionPro.ColorExtractor
CogColorMatchTool Cognex.VisionPro.ColorMatch
CogColorSegmenterTool Cognex.VisionPro.ColorSegmenter
CogFindCircleTool Cognex.VisionPro.Caliper
CogFindLineTool Cognex.VisionPro.Caliper
CogCreateCircleTool Cognex.VisionPro.Dimensioning
CogDistancePointPointTool Cognex.VisionPro.Dimensioning
Cog2DSymbolTool Cognex.VisionPro.TwoDSymbol
CogBarcodeTool Cognex.VisionPro.Barcode
CogOCVTool Cognex.VisionPro.OCV
CogAffineTransformTool Cognex.VisionPro.ImageProcessing
CogHistogramTool Cognex.VisionPro.ImageProcessing
CogImageSharpnessTool Cognex.VisionPro.ImageProcessing
CogSobelEdgeTool Cognex.VisionPro.ImageProcessing

6. CogMisc实用工具方法

CogMisc 是VisionPro提供的静态工具类,包含大量几何计算方法,常与序列化后的对象配合使用:

方法 签名 说明
AngleLineLine static double AngleLineLine(CogLine lineA, CogLine lineB, ICogImage image) 返回从 LineA 到 LineB 的夹角(弧度)
AnglePointPoint static double AnglePointPoint(double startX, double startY, double endX, double endY) 返回两点连线的角度(弧度)
DistancePointLine static double DistancePointLine(double x, double y, CogLine line, ICogImage image, out double lineX, out double lineY) 点到直线的最短距离
DistancePointPoint static double DistancePointPoint(double startX, double startY, double endX, double endY, out double angle) 两点之间的距离
DistancePointSegment static double DistancePointSegment(double x, double y, CogLineSegment segment, ICogImage image, out double segmentX, out double segmentY) 点到线段的最短距离
InsidePointCircle static bool InsidePointCircle(double x, double y, CogCircle circle, ICogImage image) 判断点是否在圆内(可用于求圆度)
IntersectLineLine static bool IntersectLineLine(CogLine lineA, CogLine lineB, ICogImage image, out int numPoints, out double x, out double y, out double angle) 判断两线是否相交
CreateLineParallel static CogLine CreateLineParallel(double x, double y, CogLine line, ICogImage image) 过指定点创建平行线
CreateLinePerpendicular static CogLine CreateLinePerpendicular(double x, double y, CogLine line, ICogImage image) 过指定点创建垂线
CreateSegmentByAveragingSegments static CogLineSegment CreateSegmentByAveragingSegments(CogLineSegment segmentA, CogLineSegment segmentB, ICogImage image) 通过平均两条线段创建新线段
DegToRad static double DegToRad(double degrees) 角度转弧度
RadToDeg static double RadToDeg(double radians) 弧度转角度

7.坐标空间特殊符号

在VisionPro中使用坐标空间时,以下特殊符号具有特定含义:

符号 描述
@ 根空间
# 像素空间
* 显示像素空间(仅在为图形指定空间名称时可用)
^ 根空间的别名
. 图像的当前选定空间名称(仅可用于 CogImage 对象的方法)
.. 当前选定空间的父空间名称
$ 父空间为其子空间创建的子坐标空间(VisionPro 图形系统使用 $ 允许形状的多代层次结构)

8. 附录

8.1 常用using语句

csharp 复制代码
using Cognex.VisionPro;                    // 核心命名空间(CogSerializer 在此)
using Cognex.VisionPro.ToolBlock;          // CogToolBlock
using Cognex.VisionPro.ToolGroup;          // CogToolGroup
using Cognex.VisionPro.PMAlign;            // 模板匹配
using Cognex.VisionPro.Caliper;            // 卡尺/找线/找圆
using Cognex.VisionPro.Blob;               // 斑点分析
using Cognex.VisionPro.ImageFile;          // 图像文件操作
using Cognex.VisionPro.CalibFix;           // 标定与定位
using Cognex.VisionPro.Dimensioning;       // 几何测量
using Cognex.VisionPro.ImageProcessing;    // 图像处理
using Cognex.VisionPro.Barcode;            // 条码识别
using Cognex.VisionPro.TwoDSymbol;         // 二维码
using System.IO;                           // Stream 相关
using System.Runtime.Serialization.Formatters.Binary; // BinaryFormatter

8.2 一句话速查

需求 代码
保存配置 CogSerializer.SaveObjectToFile(obj, "path.vpp");
加载配置 var obj = CogSerializer.LoadObjectFromFile("path.vpp") as CogToolBlock;
深度复制 var copy = CogSerializer.DeepCopyObject(source) as CogToolBlock;
完整备份 CogSerializer.SaveObjectToFile(obj, path, null, CogSerializationOptionsConstants.All);
迁移导出 CogSerializer.SaveObjectToFile(obj, path, null, CogSerializationOptionsConstants.ExcludeDataBindings);
流式复制 CogSerializer.SaveObjectToStream(obj, ms); ms.Position = 0; var copy = CogSerializer.LoadObjectFromStream(ms);
相关推荐
不会编程的懒洋洋3 天前
VisionPro 中 几何相交工具 Geometry-Intersection
图像处理·笔记·c#·视觉检测·机器视觉·visionpro
不会编程的懒洋洋3 天前
VisionPro 中 图像预处理工具
图像处理·笔记·c#·视觉检测·visionpro
爱凤的小光8 天前
VisionPro之校准与定位工具
visionpro
爱凤的小光9 天前
VisionPro通用工具---个人学习篇
visionpro
爱凤的小光10 天前
VisionPro之工具窗体设置控件一
visionpro
不会编程的懒洋洋11 天前
VisionPro 中 卡尺 CogCaliperTool
图像处理·人工智能·笔记·计算机视觉·visionpro·cogcalipertool
不会编程的懒洋洋11 天前
VisionPro 中 直方图 CogHistogramTool
图像处理·人工智能·笔记·计算机视觉·机器视觉·visionpro·康耐视
预见AI1 个月前
康耐视VisionPro连接海康相机教程(Gige)及常见错误问题
人工智能·计算机视觉·visionpro·海康相机
幻想趾于现实2 个月前
CogFitLineTool 工具调用多个卡尺得分
机器学习·visionpro