Skip to content

Repository files navigation

CNS.Core.Configuration

.NET Avalonia License

半导体 / OLED 检测量测设备的三层级配置管理框架,提供系统级 → 设备级 → 配方级完整的参数管理能力,支持版本迁移、快照差异、AES 加密、SECS/GEM 参数映射及 Avalonia UI 集成。


目录


架构概览

┌─────────────────────────────────────────────────┐
│               应用层 (Demo / 设备应用)             │
├─────────────────────────────────────────────────┤
│        CNS.Core.Configuration.Autofac           │  ← DI 集成 / 文件注入
│        CNS.Core.Configuration.Mvvm              │  ← MVVM 绑定基类
├─────────────────────────────────────────────────┤
│           CNS.Core.Configuration                │  ← 核心库
│  ┌──────────┬──────────┬────────────────────┐  │
│  │ System   │  Base    │      Recipe        │  │
│  │ 系统配置  │ 设备配置  │  配方配置(可切换)  │  │
│  └──────────┴──────────┴────────────────────┘  │
│  持久化 / 验证 / 快照 / 审计 / 加密 / 版本迁移     │
└─────────────────────────────────────────────────┘

三层级说明

层级 对象类型 存储路径 可切换 典型用途
System ISettingsFileOperation 单一 JSON 文件 ✗ 软件平台参数、端口、路径
Base IBaseConfigure 集合 base.json ✗ 相机标定、运动轴参数、光源参数
Recipe IRecipeConfigure 集合 recipes/<id>.json ✓ 视觉算法、检测阈值、工艺参数

指针稳定性:切换配方时通过 IObjectMapper 原地属性赋值,DI 容器中的对象引用始终不变,UI 绑定无需重建。


项目结构

CNS.Core.Configuration/
├── src/
│   ├── CNS.Core.Configuration/               核心库(net462;net8.0)
│   │   ├── Abstractions/                     23 个核心接口
│   │   ├── Base/                             ConfigureBase、Composite/List 变体
│   │   ├── Store/                            ConfigurationStoreBase、InMemoryContainer
│   │   ├── Recipe/                           RecipeConfiguration、RecipeMetadata
│   │   ├── Serialization/                    ConfigureJsonConverter、报告
│   │   ├── Validation/                       ValidationPipeline、跨节点验证
│   │   ├── ChangeDetection/                  快照、Undo/Redo、变更检测
│   │   ├── Events/                           ConfigEventBus、ConfigChangedEvent
│   │   ├── Audit/                            FileAuditTrailService
│   │   ├── Security/                         访问控制、AES 加密、锁定
│   │   ├── Backup/                           双路径备份服务
│   │   ├── Migration/                        链式版本迁移
│   │   ├── Mapping/                          集合映射策略
│   │   ├── Secs/                             SECS/GEM 参数注册
│   │   ├── Format/                           XML/CSV/SECS 格式转换
│   │   ├── Diagnostics/                      健康报告、性能指标
│   │   ├── SystemConfig/                     JsonFileOperation
│   │   └── Bootstrapper/                     三层级初始化编排
│   │
│   ├── CNS.Core.Configuration.Mvvm/          MVVM 绑定层(net462;net8.0)
│   │   └── BindableConfigureBase             SetProperty 自动标记 IsDirty
│   │
│   └── CNS.Core.Configuration.Autofac/       DI 集成(net462;net8.0)
│       ├── ConfigurationServiceModule        一次性服务注册
│       ├── FileInjectionModule               JSON 驱动的依赖注入
│       └── ConfigureContainerPopulator       IStartable 自动填充
│
├── tests/
│   ├── CNS.Core.Configuration.Tests/         单元测试(net8.0)
│   └── CNS.Core.Configuration.Autofac.Tests/ Autofac 集成测试(net8.0)
│
├── demo/
│   └── CNS.Core.Configuration.Demo/          Avalonia 演示应用(net8.0)
│
└── docs/                                     设计文档

功能特性

# 特性 说明
1 配方切换 按产品/批次切换,指针不变,UI 绑定不刷新
2 配方库 CRUD 创建、复制、删除、导入、导出、历史记录
3 快照与差异 任意时刻截取配置状态,对比两个快照的属性变化
4 属性验证 Critical / Error / Warning 三级,支持 [ParameterRange] 标注
5 Undo / Redo 会话级命令模式,支持多步撤销/重做
6 AES-256 加密 [Sensitive] 标注属性自动加密存储($enc:AES256:base64)
7 访问控制 Operator / Engineer / Admin 三级角色,按层级授权
8 配置锁定 生产运行期间阻止任何修改
9 审计追踪 追加写入审计日志(谁、何时、改了什么、改成什么)
10 双路径备份 本地路径 + AppData 同步备份,崩溃恢复 _working.json
11 链式版本迁移 按版本号自动升级旧配置文件
12 SECS/GEM 参数映射 [SecsParameter] 属性标注,支持 S2F13 查询 / S2F15 设置
13 格式转换 一键导出 XML / CSV / SECS Binary
14 文件注入 JSON 驱动的 Autofac 构造函数/属性/集合注入
15 批量操作 BeginBatch/EndBatch 合并事件,单次 UI 刷新

快速开始

1. 安装 NuGet 包

<!-- 核心库 -->
<PackageReference Include="CNS.Core.Configuration" Version="*" />

<!-- MVVM 支持(Avalonia / WPF) -->
<PackageReference Include="CNS.Core.Configuration.Mvvm" Version="*" />

<!-- Autofac DI 集成(可选) -->
<PackageReference Include="CNS.Core.Configuration.Autofac" Version="*" />

2. 定义配置节点

// 设备级配置(Base 层)
public class CameraConfig : BaseConfigureBase
{
    private double _exposureTime = 5000;

    [ParameterRange(100, 100000)]
    public double ExposureTime
    {
        get => _exposureTime;
        set => SetConfigProperty(ref _exposureTime, value);
    }
}

// 配方级配置(Recipe 层)
public class InspectionAlgorithmConfig : RecipeConfigureBase
{
    private double _threshold = 0.5;

    [ParameterRange(0.0, 1.0)]
    [SecsParameter(id: 101, kind: SecsParameterKind.Recipe, dataType: "F8")]
    public double Threshold
    {
        get => _threshold;
        set => SetConfigProperty(ref _threshold, value);
    }
}

3. 注册与初始化(Autofac)

var builder = new ContainerBuilder();
builder.RegisterModule(new ConfigurationServiceModule(options =>
{
    options.BaseFilePath    = @"Config\base.json";
    options.RecipeDirectory = @"Config\recipes";
    options.EnableAudit     = true;
    options.EnableBackup    = true;
}));

// 注册配置节点
builder.RegisterType<CameraConfig>()
       .As<IBaseConfigure>()
       .SingleInstance();

builder.RegisterType<InspectionAlgorithmConfig>()
       .As<IRecipeConfigure>()
       .SingleInstance();

var container = builder.Build();

4. 切换配方

var recipe = container.Resolve<IRecipeConfiguration>();

// 切换配方(原地属性赋值,指针不变)
var result = await recipe.SwitchAsync("aoi-highsens-v1");
if (result.IsFailure)
    logger.Error("切换失败: {Error}", result.Error);

核心概念

ConfigureBase 层次结构

ConfigureBase                   ← Key(确定性 GUID)/ Name / Version / IsDirty / Clone / Validate
├── BaseConfigureBase           ← 设备级标记(implements IBaseConfigure)
├── RecipeConfigureBase         ← 配方级标记(implements IRecipeConfigure)
├── CompositeConfigureBase<T>   ← 含 Children 集合(ICompositeConfigure)
└── ListConfigureBase<T>        ← 列表容器(List<T> + ICompositeConfigure)

MVVM 变体(CNS.Core.Configuration.Mvvm):
└── BindableConfigureBase       ← SetProperty → IsDirty = true → 发布 ConfigValueChanged 事件
    └── BindableCompositeConfigureBase<T>
    └── BindableListConfigureBase<T>

指针稳定性原理

DI 容器注册时: container[InspectionAlgorithmConfig] → 0x1A2B3C (固定地址)
                                                           ↑
配方切换时:  RecipeConfiguration.SwitchAsync("recipe-b")  │
              └→ DeserializeAllAsync(filePath)             │
              └→ ApplySections(sections)                   │
                   └→ MapProperties(loaded, registered)    │
                        └→ Mapster.Map(src → dst)  ─────────┘
                             仅拷贝属性值,对象地址不变

事件总线

// 订阅属性变更
eventBus.Subscribe<ConfigValueChangedEvent>(evt =>
{
    logger.Info("{Property}: {Old} → {New}", evt.PropertyName, evt.OldValue, evt.NewValue);
});

// 批量操作(合并事件)
using (eventBus.BeginBatch())
{
    config.Threshold = 0.3;
    config.ExposureTime = 8000;
}   // EndBatch 时统一触发一次

接口参考

IConfigure — 配置节点契约

public interface IConfigure : INamed, IKeyed, IVersioned
{
    string Name      { get; set; }
    Guid   Key       { get; }          // 确定性 GUID(由类型全名生成)
    void   RestoreDefault(object? param = null);
}

IConfigurationStore — 持久化存储

public interface IConfigurationStore : IConfigureContainer
{
    string FilePath     { get; }
    ConfigurationTier Tier { get; }
    Task<RecordResult> SaveAsync(CancellationToken ct = default);
    Task<RecordResult> LoadAsync(CancellationToken ct = default);
}

IRecipeConfiguration — 配方存储

public interface IRecipeConfiguration : IConfigurationStore
{
    string         CurrentRecipeId  { get; }
    RecipeMetadata CurrentMetadata  { get; }

    Task<RecordResult> SwitchAsync(string recipeId, CancellationToken ct = default);
    Task<RecordResult> SaveAsAsync(string newRecipeId, RecipeMetadata metadata, CancellationToken ct = default);
}

IRecipeLibrary — 配方库管理

public interface IRecipeLibrary
{
    IReadOnlyList<RecipeMetadata> AllRecipes { get; }

    Task<RecordResult<string>>          CreateAsync(RecipeMetadata metadata, IReadOnlyList<IConfigure> sections);
    Task<RecordResult>                  UpdateMetadataAsync(string recipeId, Action<RecipeMetadata> update);
    Task<RecordResult>                  DeleteAsync(string recipeId);     // 软删除
    Task<RecordResult>                  ImportAsync(string sourcePath);
    Task<RecordResult>                  ExportAsync(string recipeId, string targetPath);
    Task<RecordResult<IReadOnlyList<RecipeHistoryEntry>>> GetHistoryAsync(string recipeId);
}

IValidationPipeline — 三级验证

public interface IValidationPipeline
{
    ValidationReport Validate(IConfigure section);
    ValidationReport ValidateAll(IReadOnlyList<IConfigure> sections);
}
// ValidationLevel: Critical | Error | Warning

Demo 应用

运行方式

cd demo/CNS.Core.Configuration.Demo
dotnet run

7 个功能 Tab

Tab 演示内容
System Config JsonFileOperation 节/键读写、预设加载
Base Config 多设备配置 + PropertyGrid + 验证 + Undo/Redo + 快照差异
Recipe Management 配方库 CRUD / 复制 / 导入导出 / 历史 / 默认值恢复
Security & Access 访问控制 + 配置锁定 + 加密字段预览
Audit & Diagnostics 审计日志查询 + 健康报告 + 性能指标
Event Bus & Batch 事件订阅/发布 + 批量操作合并事件演示
Format & Bootstrapper XML/CSV/SECS 导出 + 双路备份 + 生命周期编排

预置配方数据

Demo 内置 3 份 AOI 配方用于验证切换配方指针稳定性:

配方 ID 配方名称 Threshold ExposureTime 算法模式
aoi-standard-v1 AOI 标准版 0.5 5000 µs PatternMatch
aoi-highsens-v1 AOI 高灵敏度 0.3 8000 µs DeepLearning+HDR
aoi-lowpower-v1 AOI 低功耗 0.7 3000 µs Edge

切换配方后,在 UI 中观察三个配置对象的 RuntimeHelpers.GetHashCode() 值,验证地址不变。


配方管理

JSON 格式规范

配方文件为节数组,每个节包含 $type 鉴别符:

[
  {
    "$type": "CNS.Core.Configuration.Demo.Models.Recipe.InspectionAlgorithmConfig, CNS.Core.Configuration.Demo",
    "Name": "检测算法配置",
    "Version": "1.0.0",
    "Threshold": 0.5,
    "MinDefectSize": 5.0,
    "SearchMode": 0
  },
  {
    "$type": "CNS.Core.Configuration.Demo.Models.Recipe.CameraCaptureSettings, CNS.Core.Configuration.Demo",
    "Name": "相机采集设置",
    "ExposureTime": 5000,
    "Gain": 1.0,
    "EnableHDR": false
  }
]

配方索引 _index.json

[
  {
    "RecipeId": "aoi-standard-v1",
    "DisplayName": "AOI 标准版",
    "Category": "AOI产品",
    "Version": "1.0.0",
    "IsDeleted": false,
    "CreatedAt": "2025-01-01T00:00:00Z",
    "FilePath": "AOI产品/AOI标准版.json"
  }
]

SearchMode 枚举

值 名称 说明
0 Spiral 螺旋扫描
1 Raster 光栅扫描
2 Edge 边缘扫描

安全与访问控制

// 声明敏感字段
[Sensitive]
public string ApiKey { get; set; } = string.Empty;
// 存储: "$enc:AES256:BASE64_ENCODED_VALUE"

// 访问控制
accessControl.EnsureCanModify("operator-001", ConfigurationTier.Recipe);
// → 仅 Engineer 和 Admin 可修改 Recipe 层

// 生产锁定
lockService.Lock("production-run-001");
// → 锁定期间任何 Set 操作均抛出 ConfigurationLockedException

SECS/GEM 集成

// 标注 SECS 参数
[SecsParameter(id: 101, kind: SecsParameterKind.Recipe, dataType: "F8",
               description: "检测阈值", unit: "None")]
public double Threshold { get; set; }

// S2F13 — 查询参数
var param = secsRegistry.GetById(101);
// param.Value = 0.5, param.DataType = "F8"

// S2F15 — 设置参数
await secsRegistry.SetValueAsync(101, 0.3);
// 内部: 找到对应属性 → 验证范围 → 写入 → 触发 ConfigValueChanged

Autofac 集成

文件注入(FileInjectionModule)

通过 JSON 文件驱动依赖注入,无需修改代码即可切换实现:

{
  "services": [
    {
      "type": "MyApp.Services.CameraService, MyApp",
      "as": "MyApp.Abstractions.ICameraService",
      "lifetime": "singleton",
      "constructor": {
        "deviceIndex": 0,
        "baudRate": 9600
      }
    }
  ]
}
builder.RegisterModule(new FileInjectionModule(@"Config\services.json"));

测试

# 运行全部测试
dotnet test

# 仅运行单元测试
dotnet test tests/CNS.Core.Configuration.Tests

# 仅运行 Autofac 集成测试
dotnet test tests/CNS.Core.Configuration.Autofac.Tests

# 生成覆盖率报告
dotnet test --collect:"XPlat Code Coverage"

测试使用 xUnit 2.6.6 + FluentAssertions 6.12.0 + Moq / NSubstitute。


依赖项

CNS.Core.Configuration(核心库)

包 版本 用途
System.Text.Json 内置 JSON 序列化/反序列化
CompareNETObjects 4.x 深度对象变更检测
CSharpFunctionalExtensions 2.x Result / Maybe 错误处理

CNS.Core.Configuration.Autofac

包 版本 用途
Autofac 8.2.0 DI 容器
Microsoft.Extensions.Configuration 9.x 配置文件读取

CNS.Core.Configuration.Demo

包 版本 用途
Avalonia 11.2.3 跨平台 UI 框架
Avalonia.Controls.DataGrid 11.2.3 DataGrid 控件
Semi.Avalonia 11.2.3 UI 主题(Semi Design)
Avalonia.PropertyGrid 11.2.x 属性编辑器控件
ReactiveUI 20.x MVVM 响应式框架
Mapster 7.4.0 对象属性映射(配方切换)
Autofac 8.2.0 依赖注入容器
Serilog 4.x 结构化日志

变更日志

详见 CHANGELOG.md。


项目规范

本项目遵循以下约定:

  • 目标框架:net462;net8.0(所有库项目必须双目标)
  • 编译开关:AllowUnsafeBlocks + ImplicitUsings + TreatWarningsAsErrors + GenerateDocumentationFile
  • 日志:Serilog,必须携带 DeviceId / RecipeId / TraceId
  • 异常:禁止空 catch {},必须向上抛出或转换为 Result.Failure
  • 硬编码:禁止硬编码阈值参数,所有参数来自 Recipe / Config
  • 硬件代码:必须提供 Mock / Simulation 模式

许可证

内部使用,版权归 CNS(深圳)视觉科技有限公司所有。

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages