项目概览
SmartEarInspect 是一套面向耳机生产线的 Windows 智能检测工作站软件。系统把治具运动、数字 IO、安全信号、CCD/AOI 字符与外观检测、颜色检测、MES 数据、质量记录和操作权限编排在同一个可追踪流程中,使操作员面对的是稳定的工位状态和检测结果,而不是分散的控制器、串口、网络与相机协议。
该系统已经在真实产线中正式使用。运动控制、IO 与安全联锁、CCD/AOI、颜色检测、MES 国别适配、权限、指纹登录、审计、检测追溯和备份能力均经过现场使用。本文只作定性总结,不公开客户、具体产品型号、产线拓扑、设备品牌、工艺参数、接口字段、地址端口、节拍、产量、良率或稳定性指标。
本文以稳定源码提交 2fd7f08 为整理基线。该基线可独立完成 Release 构建,并通过全部 500 项自动化测试。正在进行但尚未进入稳定基线的本地重构不作为本文成果。
项目背景与目标
耳机字符、颜色和外观检测并不是“拍一张照片再显示 PASS/NG”这么简单。完整工位需要同时处理:
- 操作员身份与权限;
- 设备连接、自检、回零与安全状态;
- SN 输入、MES 查询和动态预期值;
- 不同国别或型号对应的检测点组合;
- 治具在多个拍摄和传感位置之间运动;
- CCD/AOI 请求、结果与图像的异步到达;
- RS485 或数字 IO 类型的颜色传感器;
- 暂停、停止、异常、断线和恢复;
- 检测记录、图片索引、审计与备份。
如果这些逻辑全部堆在 WPF 窗口事件中,硬件状态、界面状态和业务状态会快速耦合。项目因此把目标确定为:用状态机表达生产流程,用接口隔离硬件差异,用配置表达可变工艺,用本地数据保存质量证据,并让没有真实设备的开发环境仍可推进功能开发。
设计原则与安全边界
系统实现遵循以下原则:
- 流程与设备分离:工作流只依赖业务语义接口,不直接调用厂商 API;
- 界面与业务分离:ViewModel 负责命令和可观察状态,View 只负责绑定与交互;
- 真实与 Mock 同契约:开发环境替换设备实现,不复制另一套业务流程;
- 配置驱动工艺:检测点、运动参数、国别规则与 IO 映射由配置装配;
- 失败必须收敛:超时、取消和异常都要回到可解释状态并执行清理;
- 结果必须留痕:检测记录、明细和关键操作进入本地持久化与审计链路;
- 最小权限操作:管理、调试、配置和生产操作按角色与功能权限控制。
上位机软件不替代安全继电器、硬接线急停、驱动器保护或设备风险评估。软件中的双手启动、安全门、光栅和停止逻辑是控制层防护,必须与现场电气安全回路共同工作。
技术栈与运行约束
| 领域 | 技术 | 主要职责 |
|---|---|---|
| 桌面运行时 | .NET 8 / C# / WPF | Windows x64 工位应用、异步流程和数据绑定 |
| 表现层 | MVVM / CommunityToolkit.Mvvm | 可观察状态、命令与 ViewModel 生成 |
| 依赖装配 | Microsoft.Extensions.DependencyInjection | 配置、服务、ViewModel 与设备实现生命周期 |
| 数据存储 | EF Core / SQLite | 用户、权限、审计、检测记录和结果明细 |
| 日志 | NLog / Microsoft.Extensions.Logging | 启动、设备、流程、异常与运维日志 |
| 外部通信 | TCP / HTTP / RS485 / 数字 IO | AOI、MES、颜色传感器和现场信号 |
| 图像通道 | MemoryMappedFile / 命名事件 | 跨进程图像帧传输与新帧通知 |
| 运动控制 | 独立 MotionControl 库 | 控制器、轴、回零、点位运动与 IO |
| 自动化测试 | xUnit / Moq | 业务、数据、通讯并发和基础设施验证 |
主程序目标框架为 net8.0-windows8.0,Release 输出面向 win-x64。真实运行还依赖设备驱动、原生运行库以及与现场一致的配置文件,因此“源码可构建”和“工位可投产”是两个不同的验收层级。
分层架构
系统以 App.xaml.cs 为装配入口,把配置、基础设施、业务服务、设备适配和 MVVM 表现层串成单向依赖链:
Operator / MainWindow
│
▼
InspectionViewModel
│ commands, progress, images, result presentation
▼
InspectionWorkflow
│ state machine, safety, motion, lifecycle
▼
InspectionService
│ point-level inspection and result aggregation
├───────────────┬────────────────┬─────────────────┐
▼ ▼ ▼ ▼
Motion + IO CCD / AOI Color Sensor MES / Rules
│ │ │ │
└───────────────┴────────────────┴─────────────────┘
│
▼
InspectionDataService
│
▼
EF Core / SQLite
核心调用关系可以概括为:
InspectionViewModel
└── InspectionWorkflow
├── InspectionService
├── IMotionControllerProvider
├── IInspectionIoService
├── IInspectionExecutionPlanBuilder
└── IInspectionDataService
InspectionViewModel 不直接读写控制卡或串口;InspectionWorkflow 不关心按钮、表格和图片控件;设备服务也不决定一个生产周期应该经过哪些状态。这种边界使 UI、工艺和设备协议能够分别演进。
启动与依赖装配
应用启动过程本身是一条受控链路:
- 注册 UI、非 UI 线程和未观察 Task 的全局异常处理;
- 加载主配置与专项配置,初始化日志目录;
- 建立依赖注入容器,注册配置、服务、ViewModel 和窗口;
- 初始化多语言服务;
- 执行 EF Core 数据库迁移、权限同步和基础数据初始化;
- 初始化运动控制提供者;
- 建立权限到 WPF 可见性和可用性的绑定;
- 解析并显示主窗口。
有运行状态的组件,例如运动控制器、检测工作流、IO 服务和当前用户上下文,使用与应用生命周期一致的单例;无状态数据访问服务通过 IDbContextFactory 按操作创建上下文,避免在长期运行的 UI 进程中共享同一个 DbContext。
检测状态机
一轮检测不是一组松散按钮事件,而是由 InspectionWorkflow 驱动的状态机:
登录与权限确认
↓
设备自检与运动轴回零
↓
输入产品标识并查询 MES
↓
解析国别规则与动态预期值
↓
等待双手启动和安全条件
↓
治具移动到检测位置
↓
按执行计划逐点运动与检测
↓
综合 PASS / NG 判定
↓
保存记录、明细和审计信息
↓
治具回原位并进入下一周期
状态机明确表达空闲、等待启动、治具运动、CCD 检测、颜色检测、判定、保存、回原位、暂停、完成和错误等状态。界面通过事件接收状态、进度、日志、单点结果和周期完成通知,不轮询内部业务对象。
调用侧只需要准备运行上下文并交给工作流,硬件步骤留在工作流与设备服务内部。以下代码为删除现场细节后的简化示意:
var selfCheck = await workflow.PreCheckDevicesAsync(cancellationToken);
if (!selfCheck.AllPassed)
{
return;
}
var record = await workflow.ExecuteAsync(runtimeContext, cancellationToken);
ShowResult(record);
手动模式执行一个周期后停止;自动模式在每轮结束后重新进入启动确认,并根据配置决定是否为下一件产品重新获取运行上下文。停止策略同时支持立即取消和完成当前周期后停止,暂停则通过显式检查点阻断后续步骤。
MES、动态预期值与国别适配
现场产品的检测内容并不总是固定。系统在设备自检完成后获取产品标识,通过可配置的 HTTP 适配器查询 MES,再把返回内容转换为统一运行上下文:
Operator input
→ MES provider
→ response parser
→ expected-value mapper
→ country profile resolver
→ inspection execution plan
运行上下文保存本周期的产品标识、动态预期值、国别信息以及允许执行的识别项。执行计划构建器先按启用状态和顺序整理检测点,再根据国别规则过滤不适用的识别项;预期值解析器优先使用本周期动态值,没有覆盖时才回退到配置值。
MES 适配器具备请求取消、HTTP 状态检查和响应编码处理。解析器会拒绝空响应、格式异常或缺少关键业务信息的返回,避免把不完整数据悄悄带入检测。本文不展示真实请求模板、响应字段、国别代码和预期内容;这些都属于现场配置和业务接口边界。
运动控制与语义 IO
SmartEarInspect 复用独立的 MotionControl 库接入工业运动控制器。上层通过 IMotionControllerProvider 管理驱动选择、初始化、连接、回零、轴配置与控制器生命周期,不在检测代码中出现厂商句柄和原始错误码。
检测点同时描述运动位置和检测类型。工作流移动到点位、等待到位,再调用对应检测服务;直线轴与旋转轴的具体单位、回零和运动细节由运动控制层负责。
物理 IO 被进一步封装为业务语义,例如:
- 左右启动按钮与按钮灯;
- 安全门、光栅和物料检测;
- 红黄绿灯与蜂鸣器;
- 颜色传感器开关量输出;
- 定位气缸、真空和破真空输出。
IO 索引、类型和反向逻辑来自配置,业务层只调用 IsSystemSafe()、WaitForDualHandStartAsync() 或 SetTriColorLight() 这类语义方法。底层监控器将数字输入变化转换为事件,减少 UI 与流程代码对物理点位的硬编码。
CCD/AOI 通讯与图像通道
视觉系统使用两条职责不同的通路:
| 通路 | 内容 | 设计重点 |
|---|---|---|
| TCP | 触发请求、PASS/NG、识别文本和扩展结果 | 超时、取消、响应完整性、断线与重连 |
| 共享内存 | 原始图像帧与帧元数据 | 双缓冲、写入完成标记、命名事件通知 |
TCP 结果通道
CCD 客户端建立长连接后,以一次完整的“发送请求 + 接收 JSON”作为不可分割的 IO 区间。单一异步锁串行化并发触发,防止不同检测点的响应交叉消费。响应读取不依赖单次网络包或固定换行,而是累积字符并在 JSON 完整后解析;超时、主动取消、空响应、非法 JSON 和连接丢失被转换为明确的检测错误。
当前实现使用操作系统 TCP KeepAlive 与 Socket 状态探测,不向不支持心跳指令的外部 AOI 注入应用层消息。检测时发现断线会进入有限次数、带间隔的重连流程,避免无限快速重试拖垮现场程序。
共享内存图像通道
图像没有附在 TCP 结果中反复编码,而是通过 MemoryMappedFile 传递。共享区包含全局头、帧槽元数据和图像数据,Writer 写入非活动槽后再切换活动索引,并通过命名事件通知 Reader。Reader 校验魔数、版本、尺寸、数据长度和写入完成状态后才发布新帧。
双缓冲减少了读写同一帧造成撕裂的概率,也使结果消息和大体积图像采用各自合适的传输机制。这里不把该方案描述为绝对“零拷贝”:托管数组、WPF 图像转换和显示过程中仍可能产生内存复制。
颜色检测
系统支持两类颜色检测路径:
- RS485 颜色传感器:读取颜色数据,根据配置选择色差算法并与目标范围比较;
- 数字 IO 颜色传感器:读取设备已完成判定后的多路开关量组合。
检测类型由点位配置决定,工作流不需要知道串口帧或 DI 地址。颜色结果与 CCD/AOI 结果使用统一的检测项模型进入综合判定和持久化,同时在界面上显示颜色、数值、名称和当前判定状态。
安全、取消与失败清理
工业工作流最重要的不是正常路径,而是异常时能否收敛。项目将以下策略放入工作流和设备层:
- 启动前检查运动控制器、轴、IO、视觉与颜色设备;
- 自动连接和回零失败时保留设备级失败原因;
- 双手启动限制两个输入的最大时间差;
- 运行中持续检查安全门与光栅,必要时暂停工作流并停止运动;
- 所有长耗时操作接收
CancellationToken; - 网络、串口和运动等待都有超时边界;
- 暂停点只阻断后续动作,不在任意位置继续发送命令;
- 立即停止与完成当前周期后停止具有不同语义;
- 异常路径停止持续 JOG、恢复指示灯并尝试让治具返回可控状态;
- 应用退出时释放服务容器、设备资源与日志系统。
软件对异常的处理目标是“停止继续扩大影响并保留诊断信息”,而不是掩盖故障后强行继续生产。无法确认安全条件时,流程保持失败或暂停状态,交由现场人员检查。
配置、方案与工艺扩展
配置由主应用设置和专项 JSON 共同组成:
Application settings
├── paths, database, logging and language
├── MES and device connection policies
└── environment selection
Inspection configuration
├── workflow mode and stop policy
├── inspection points and ordering
├── country profiles and expected values
├── IO semantic mappings
└── shared-memory image contract
Motion configuration
├── driver selection
├── axes and mechanical parameters
└── controller initialization options
检测方案通过注册表和独立方案文件管理,使产品切换主要发生在配置层。设置界面负责编辑、校验和保存,运行时服务读取强类型配置。复杂工艺变化仍需要版本评审和现场验证,不能因为配置可编辑就跳过安全检查。
数据、权限与可追溯性
系统使用 EF Core 与 SQLite 保存本地生产和管理数据,启动时自动执行迁移并同步权限定义。主要数据域包括:
- 检测记录、单点结果、图片索引和周期信息;
- 用户、角色、模块权限与功能权限;
- 用户操作审计日志;
- 指纹身份与用户之间的关联信息。
权限不只在业务方法末尾检查,还通过 WPF 绑定控制模块可见性和功能可用性。生产操作、配置、用户管理、调试、记录查询和备份入口可以分配给不同角色。指纹设备用于现场快速登录,密码认证保留为另一条受控入口。
审计服务记录关键操作的操作者、动作类型、目标和时间。备份服务支持完整与增量备份以及恢复流程,覆盖数据库和被纳入策略的业务文件。多语言服务统一界面文本与日志文化,使现场操作和故障定位使用一致的语言上下文。
这些管理能力已经进入现场使用,但本文不公开用户结构、权限清单、指纹模板、数据库文件、备份位置或默认账户信息。
Mock 与无硬件开发
应用根据运行环境为相同接口注入真实实现或 Mock:
| 能力 | Development | Production |
|---|---|---|
| 运动控制 | 内存模拟控制器与轴 | 真实工业控制器 |
| CCD/AOI | 可配置的模拟结果与图像 | TCP 视觉服务与共享内存 |
| 颜色检测 | 模拟颜色与判定 | RS485 或数字 IO 传感器 |
| MES | 模拟业务响应 | 现场 HTTP 服务 |
| 工位 IO | 模拟输入输出状态 | 控制器数字 IO |
Mock 使界面、状态机、配置、记录和异常分支能够在没有设备时开发,也便于构造超时、NG、断线和安全信号变化。它不能证明真实相机曝光、机械精度、现场电气时序或驱动稳定性,最终仍需要实机与硬件在环验证。
自动化测试策略
测试项目使用 xUnit、Moq 和临时 SQLite/文件目录验证关键行为,稳定基线共有 500 项测试:
- 工作流自检、状态变化、暂停、停止、取消和异常清理;
- 单点检测、颜色判定、预期值覆盖和综合结果;
- MES 解析、国别规则和执行计划过滤;
- 检测记录写入、查询、统计和并发数据访问;
- TCP 本地回环服务器下的串行、并发、慢响应和断线场景;
- 共享内存 Writer/Reader、帧元数据与并发读写;
- IO 语义映射、反向逻辑、双手启动和安全信号;
- 用户、登录、权限、审计、设置与备份恢复。
TCP 并发测试会启动本地回环服务,验证多个触发请求在单一 IO 锁下不会串包;共享内存测试验证帧槽切换和数据完整性;数据集成测试使用隔离数据库验证真实 EF Core 行为,而不是只验证 Mock 调用次数。
Mock 测试、集成测试和实机测试解决的是不同风险,三者不能互相替代。
构建、安装与现场交付
稳定基线的验证结果如下:
| 验证项 | 结果 |
|---|---|
| 源码基线 | 2fd7f08 |
| Release 构建 | 通过,0 error |
| 自动化测试 | 500 passed、0 failed、0 skipped |
| 分析器 | 1 条 xUnit 阻塞式 Task 使用警告 |
| 目标平台 | Windows x64 / .NET 8 |
解决方案包含 WPF 主程序、xUnit 测试项目和 Windows 安装工程。现场交付除了应用二进制,还需要匹配的设备驱动、原生依赖、数据库迁移、工艺方案和站点配置。公开仓库或博客内容不携带这些现场资产。
一次可控的交付流程应包括:
- 从锁定源码与依赖执行 Release 构建和自动化测试;
- 生成带版本号的 Windows x64 安装产物;
- 在隔离工位验证数据库升级和配置兼容性;
- 完成设备自检、回零、安全 IO、视觉、颜色和 MES 联调;
- 使用标准样件验证 PASS、NG、超时和断线分支;
- 备份当前程序、数据库和配置后再切换版本;
- 保留上一版本安装包和恢复说明。
真实产线使用结果
项目已经将原本分散的运动、视觉、传感器和管理功能整合为一套产线工作站,并在正式生产中使用。可以确认的工程结果包括:
- 操作员可以从统一界面完成登录、设备确认、启动、检测和结果查看;
- MES 数据可以在周期开始前驱动预期值和国别检测计划;
- 运动、IO、安全、CCD/AOI 与颜色检测按同一状态机协同;
- 检测结果、图片索引、用户操作和配置管理形成可查询记录;
- 指纹、角色权限、审计和备份覆盖现场管理需求;
- Mock 与自动化测试支持无硬件开发,实机验收负责验证现场时序和设备行为。
这里没有公布节拍改善比例、产量、良率、故障率或长期在线时间,因为当前没有适合公开且可复核的数据集。实际投产不等同于对外发布性能基准。
已知边界
| 边界 | 当前影响 | 改进方向 |
|---|---|---|
| 一项测试使用阻塞式 Task 操作 | 构建产生 xUnit 分析警告 | 改为完整异步测试并把分析警告纳入门禁 |
| MotionControl 使用相邻源码项目引用 | 独立拉取后不能直接构建完整解决方案 | 发布版本化内部包并锁定兼容范围 |
| Windows x64 与原生驱动依赖 | 无法跨平台运行,部署依赖现场环境 | 建立驱动、运行库和安装包兼容矩阵 |
| 设备协议文档存在演进成本 | 文档可能仍描述旧的应用层心跳 | 以现行 TCP KeepAlive 行为更新并做协议契约测试 |
| 自动化测试不能覆盖机械与电气 | 模拟通过不代表设备一定安全可靠 | 增加标准样件和硬件在环回归清单 |
| 依赖和安装产物尚未完全统一版本化 | 升级与回滚需要更多人工核对 | 建立 CI、版本号、安装包校验和发布记录 |
此外,现场配置和数据库包含业务数据,必须独立于源码管理。日志需要限制敏感内容并配置保留周期,备份必须验证可恢复性,而不是只确认压缩包已经生成。
后续路线
- 消除剩余测试分析警告,将警告策略纳入持续集成;
- 把 MotionControl 从相邻源码引用升级为可追踪的版本化依赖;
- 为 TCP、共享内存和 MES 适配器建立更明确的协议契约版本;
- 建立标准样件、断线、安全信号和设备恢复的硬件在环回归;
- 自动执行还原、Release 构建、500 项测试和安装包生成;
- 为现场升级增加版本目录、配置迁移、健康检查和可操作回滚;
- 建立日志、数据库增长、备份结果和设备离线的基础监控。
项目复盘
SmartEarInspect 最重要的工程价值,不是把更多功能放进一个 WPF 窗口,而是为多设备协同建立了清晰边界:ViewModel 表达交互,状态机表达生产流程,设备接口表达现场能力,运行上下文承载每件产品的动态规则,数据层保存可追溯结果。
项目也说明了工业软件质量不能只用“能构建”或“有很多测试”衡量。500 项测试可以显著降低业务和并发回归风险,但真正的交付仍要面对机械、电气、原生驱动、网络、操作习惯和版本回滚。把这些限制写进架构和发布流程,与实现正常检测路径同样重要。