项目概览

MES 数据采集与追溯系统是一套已经投入生产使用的 Windows 工业软件,部署在生产现场,连接 PLC 与外部 MES。它负责接收设备侧生产指令,把 RFID、产品身份和工序结果组织成可查询的本地记录,并在外部系统可用时完成校验、状态查询和结果上报。

现场数据链路并不只是“收到一条报文后转发”。一件产品从首次绑定开始,会经历校验、激光加工、清洗、视觉检测、查询和解绑等操作;设备可能重复发送同一动作,网络可能临时中断,外部接口也可能超时。系统必须同时处理身份关联、幂等、追溯、离线缓冲、重试和人工恢复,才能让设备动作与业务记录保持可解释。

本文依据当前现场版本整理:外层交付仓库基线为 c12e508,应用源码基线为 68137a6,Windows x64 安装包版本为 1.6.6。这些版本已经确认用于生产,但本文只公开通用业务和软件架构,不公开现场拓扑、客户信息或接口细节。

匿名化与公开边界

本文会说明 RFID 绑定、校验、激光、清洗、视觉检测和人工补站等通用业务,但不会公开:

  • 客户、公司、产品和设备厂商名称;
  • 真实工位编码、产品编号、RFID 数据和生产记录;
  • PLC 型号、网络地址、端口和现场连接拓扑;
  • 报文字段、完整命令格式、接口地址、请求或响应样本;
  • API Key、账号、密码、连接字符串和数据库内容;
  • 安装目录、备份目录、日志样本和现场配置;
  • 产量、节拍、良率、停机时间和未经记录的稳定性指标。

下文中的架构图、表格和流程均使用抽象名称,目的是解释工程设计,不能用于还原真实产线。

项目目标

系统围绕“现场连续生产、数据可追溯、外部系统可恢复”建立以下目标:

  1. 以稳定的 TCP 服务接收多台 PLC 的数据采集与状态查询请求;
  2. 建立 RFID 与产品身份的有效绑定关系,并正确处理重复绑定和换码;
  3. 把每个工序结果写入本地数据库,形成从产品到工位和时间的追溯链;
  4. 在外部 MES 暂时不可用时保留待上传数据,不阻塞本地证据记录;
  5. 为操作人员提供实时监控、追溯查询、报表和异常补录界面;
  6. 通过本地用户认证、管理员能力和日志降低误操作风险;
  7. 支持 Windows 现场安装、配置保留、数据库迁移和定时备份;
  8. 用单元测试与集成测试覆盖协议、处理器、持久化和外部接口适配。

技术栈与职责

层次 技术与组件 主要职责
桌面界面 .NET 8、WPF、CommunityToolkit.Mvvm 登录、导航、实时监控、追溯、报表、配置和人工补站
应用编排 Generic Host、依赖注入、命令处理器 生命周期、作用域管理、命令分发和业务用例编排
设备通信 TCP、会话管理、协议解析 连接接入、报文分帧、请求解析、顺序处理和回复
业务模型 绑定、生产记录、校验状态、上传队列 产品身份、工序证据、换码、查询和外部同步状态
数据访问 EF Core、SQLite、Repository 本地持久化、索引、迁移、WAL 和查询
外部集成 HttpClient、JSON、API Key 外部 MES 校验、结果上报和动态信息查询
后台任务 BackgroundService 队列重试、磁盘保护、数据库热备份和过期备份清理
可观测性 NLog、通信日志、运行状态 通信诊断、业务错误、接口调用和磁盘状态
验证与交付 xUnit、Moq、FluentAssertions、Inno Setup 单元/集成测试、Windows x64 安装和升级

应用使用 Generic Host 统一管理配置、日志、依赖注入和后台服务。界面通过 ViewModel 调用应用服务,设备通信和外部接口实现放在基础设施层,业务处理器只依赖仓储与适配器接口。

总体架构

┌────────────────────────── 现场设备层 ──────────────────────────┐
│ PLC / RFID / 工序设备                                          │
└──────────────────────────────┬─────────────────────────────────┘
                               │ 受控工业网络 / TCP
                               ▼
┌────────────────────────────────────────────────────────────────┐
│ .NET 8 应用                                                     │
│                                                                │
│ TcpServer → ClientSession → ProtocolParser → CommandDispatcher │
│                                             │                  │
│              ┌──────────────────────────────┼──────────────┐   │
│              ▼                              ▼              ▼   │
│       绑定/解绑处理器                 校验/查询处理器   工序处理器 │
│              │                              │              │   │
│              └──────────────────────────────┼──────────────┘   │
│                                             ▼                  │
│                                  EF Core Repository / SQLite   │
│                                             │                  │
│                     ┌───────────────────────┴──────────────┐   │
│                     ▼                                      ▼   │
│            外部上传队列与重试服务                  WPF/MVVM 界面 │
└──────────────────────────────┬─────────────────────────────────┘
                               │ HTTP / JSON
                               ▼
                     ┌───────────────────┐
                     │ 外部 MES          │
                     └───────────────────┘

现场控制与业务集成保持分层:TCP 链路面向设备,SQLite 保存本地生产事实,HTTP 适配器负责外部系统交互,WPF 界面负责人工查看和维护。外部 MES 不可用时,本地记录和待上传队列仍然保留,避免把短时网络故障直接等同于本地数据丢失。

启动与运行生命周期

应用启动时先建立单实例保护,避免同一台主机重复打开多个采集进程。随后加载配置和日志,初始化 SQLite 数据库并执行迁移,再显示本地登录窗口。只有登录成功后,Generic Host 才启动 TCP 服务、上传重试和备份等后台任务,并显示主窗口。

启动进程
  → 获取单实例锁
  → 加载配置与日志
  → 数据库迁移 / WAL 初始化
  → 本地用户登录
  → 启动 Generic Host
  → 启动 TCP、重试与备份后台服务
  → 显示生产监控界面

退出时 Host 会在限定时间内停止后台服务并释放网络、数据库和依赖注入资源。该顺序让数据库准备早于设备请求,也避免未完成登录时提前开放业务处理链路。

TCP 会话与报文边界

TCP 是字节流,不天然保留消息边界。系统同时兼容传统分隔符报文和长度前缀报文,并可在兼容模式下自动识别当前连接使用的分帧方式。会话读取器负责处理粘包、拆包、无效前缀和缓冲区恢复,再把完整载荷交给协议解析器。

关键保护包括:

  • 可配置的最大连接数和最大帧长度;
  • 空闲连接超时,避免失效连接长期占用会话;
  • 长度前缀残帧超时与兼容处理;
  • 无效帧丢弃、重新同步和标准失败回复;
  • 多个客户端会话并发运行;
  • 同一会话中的完整消息依次处理并依次回复;
  • TCP KeepAlive 与优雅停止。

每个连接拥有独立会话,但同一连接中的消息不会并行穿过处理器。这降低了同一 PLC 连续命令乱序回复的风险,同时允许不同设备独立推进。

指令分发与业务能力

协议解析器把报文转换为包含来源、序号、命令类型和参数的命令对象。分发器根据命令类型创建对应作用域和处理器,使每个请求获得独立的仓储上下文。

能力 业务含义 主要结果
绑定 建立 RFID 与产品身份的当前关联 有效绑定、工序顺序和本地生产记录
校验 向外部 MES 确认产品是否允许继续 保存最近一次有效校验状态
查询 汇总绑定、工序、颜色和校验信息 返回当前产品状态快照
解绑 结束 RFID 的当前产品关联 关闭有效绑定并保留历史
激光 记录激光工序位置和结果 产品工序追溯记录与待上传项
清洗 记录清洗工序位置和结果 产品工序追溯记录与待上传项
视觉检测 记录检测位置与判定 质量结果、追溯记录与待上传项

公开案例只说明命令职责,不展示实际命令名称、字段顺序、结果编码或报文样本。

RFID 绑定、换码与幂等

绑定处理器以数据库唯一约束作为并发冲突的最终防线。新 RFID 首次进入时创建有效绑定;同一个 RFID 重复绑定到同一产品时返回成功,避免设备重发造成重复业务;如果已经绑定到另一个产品,则关闭旧绑定、建立新绑定并记录换码结果。

收到绑定请求
  → 校验必要参数
  → 尝试创建有效绑定
  ├─ 创建成功:记录绑定结果并准备外部上报
  └─ 唯一约束冲突
       ├─ 当前产品相同:按幂等成功处理
       └─ 当前产品不同:关闭旧绑定 → 新建绑定 → 记录换码

解绑同样具有幂等语义:没有有效绑定时可直接视为已完成;存在绑定时还会核对产品身份,防止错误请求结束其他产品的关系。

产品绑定还保存头尾顺序和加工顺序,使后续查询能够恢复设备继续生产所需的业务上下文。本文不展示这些字段的现场取值和编码规则。

校验、查询与状态持久化

校验处理器根据当前用户和产品上下文生成外部请求,并把成功、拒绝或通信失败映射为有限的本地结果。最新校验状态会写入产品记录,后续查询不依赖内存中的临时变量,因此应用重启后仍能返回已经持久化的最近状态。

查询处理器围绕有效绑定聚合:

  • 产品与 RFID 关联;
  • 激光和清洗的最近有效位置;
  • 视觉检测位置与判定;
  • 头尾顺序和加工顺序;
  • 按需从外部 MES 查询的动态信息;
  • 最近一次有效校验结果。

查询结果来自本地记录与外部动态信息的组合。外部查询失败时使用明确的未知状态,而不是把网络异常伪装成业务通过。

工序数据与人工补站

激光、清洗和视觉检测处理器把工位、产品、时间、位置和结果写入统一的生产记录模型。界面上的产品追溯页可以按产品查看绑定与工序历史,报表页对生产记录进行时间和结果维度的聚合,实时监控页展示设备连接与采集状态。

对于经过现场确认的补录或返修场景,系统提供人工补站入口。补站服务会复用业务校验、载荷生成和持久化能力,并记录执行结果。它用于处理受控异常,不应绕过现场审批,也不能用来批量修改原始生产证据。

设备工序请求
  → 参数与当前绑定校验
  → 写入本地 ProductionRecord
  → 按配置创建外部上传队列记录
  → 回复设备

人工补站
  → 登录用户确认
  → 输入产品与工序上下文
  → 应用服务校验并执行
  → 记录本地结果和外部调用状态

外部 MES 适配与失败重试

外部适配器使用 HttpClient 发送 JSON 请求,支持结果上报、产品校验和动态信息查询。接口地址、认证信息和超时由配置提供,运行时可读取更新后的外部 MES 设置。适配器区分 HTTP 失败、超时、主动取消、空响应和无法解析的响应,并返回有限的业务结果。

需要异步上报的数据先写入 SQLite 上传队列。后台服务定期按创建时间读取待处理或仍可重试的失败项,调用外部适配器,并根据结果更新状态、重试次数、最近重试时间和错误信息。失败项使用指数退避,避免外部系统故障时持续高频请求;磁盘空间低于保护阈值时暂停处理,避免数据库和日志继续放大磁盘风险。

本地工序记录
  → 创建 Pending 上传项
  → 后台服务轮询
  → 调用外部 MES
  ├─ 成功:标记 Succeeded
  └─ 失败:记录原因和次数
             → 指数退避
             → 未超过上限时再次处理

这套机制提供了离线缓冲和基本重试,但当前实现不是严格的事务型 Outbox,相关一致性风险会在“已知边界”中单独说明。

WPF 运维与追溯界面

桌面端使用 WPF、MVVM 和依赖注入组织页面。页面在加载时创建自己的作用域,卸载时释放 ViewModel 和数据库资源,避免长生命周期界面持有已经失效的上下文。

主要界面包括:

  • 实时监控:设备连接、采集状态和最近活动;
  • 产品追溯:绑定历史、工序记录和综合状态;
  • 生产报表:按时间、结果和工序聚合,并通过 LiveCharts 展示;
  • 产品补站:执行经过授权的异常补录;
  • 系统配置:维护 TCP、外部 MES、监控设备和日志相关设置;
  • 用户管理:创建、启停和维护本地用户;
  • 运行诊断:查看通信日志、接口异常和磁盘状态。

界面是生产数据的查看和维护入口,但数据库记录与设备消息才是业务事实来源。监控页不会因为界面显示“已发送”就推断外部系统已经接受数据。

本地认证与用户管理

应用启动后要求本地账户登录。密码使用随机盐和 PBKDF2-SHA256 保存,并通过固定时间比较验证,不在数据库中保存明文。用户记录包含启用状态、管理员标识、创建时间和最后登录时间,管理员可以维护现场账户。

本地登录保护的是桌面操作入口,并不为 PLC TCP 连接提供身份认证。管理员标识也只是当前应用内的权限边界,不等同于具有模块、动作和数据范围控制的完整 RBAC。账号停用、密码轮换、失败锁定和现场交接仍需要配套管理制度。

SQLite、迁移与备份

系统以 SQLite 保存绑定、生产记录、通信日志、上传队列和用户数据。EF Core 迁移在后台服务启动前执行,WAL 模式改善界面查询、设备写入和后台任务并行时的使用体验。仓储通过 IDbContextFactory 为操作创建独立上下文,减少长生命周期上下文带来的并发和跟踪问题。

备份后台服务按小时使用 SQLite 原生热备份能力创建一致性副本,并清理超过本地保留期的旧文件。备份过程中不需要停止正常读写,但当前备份仍位于本机;主机级故障、磁盘损坏和勒索风险需要通过异机副本与实际恢复演练解决。

Windows 安装与现场交付

应用面向 Windows x64,通过 Inno Setup 生成版本化安装程序。安装工程包含主程序、运行依赖和默认配置,并在升级时保留现场已有的应用配置,避免覆盖已经验收的网络与外部系统参数。

确认源码与依赖版本
  → Release 构建
  → 运行单元测试和集成测试
  → 发布 Windows x64 应用
  → 生成 Inno Setup 安装包
  → 备份现场配置和数据库
  → 安装或升级
  → 验证登录、PLC 通信、追溯和外部 MES

配置保留可以降低升级风险,但也可能让旧配置缺少新字段。因此安装包版本、配置 schema、数据库迁移和现场验收记录需要通过统一发布清单关联。

构建与测试基线

本案例编写前在当前现场源码版本上重新执行了 Release 构建和测试:

验证项 结果
外层交付仓库 c12e508,工作区干净
应用源码仓库 68137a6,工作区干净
Windows x64 安装包 1.6.6,确认为当前现场版本
.NET Release 构建 0 error、0 warning
自动化测试 两个测试项目,192 项通过、0 项失败
源码仓库 CI 当前没有持续集成工作流

测试覆盖协议解析、命令处理器、绑定幂等、外部接口适配、SQLite 仓储、用户认证、界面 ViewModel 和 TCP 集成场景。测试全绿说明当前验证范围内的行为一致,不代表真实 PLC、现场网络和外部 MES 可以省略联调。

生产使用结果

根据已经确认的现场事实,可以做出以下定性描述:

  • 当前源码与安装包版本已经投入生产使用;
  • PLC 数据采集、RFID 产品绑定和工序追溯形成了完整本地链路;
  • 校验、查询、激光、清洗和视觉结果能够进入统一业务模型;
  • 外部 MES 同步失败时可以通过本地队列保留并重试;
  • 操作人员可以在桌面端监控、查询、报表分析和执行受控补站;
  • Windows 安装工程用于现场安装、升级和配置保留。

本文不披露客户和产品,也不虚构产量、节拍、良率、接口可用率或长期稳定性指标。源码中存在某项能力,并不表示所有工位都启用了该能力。

已知边界与风险

通信安全

  • PLC TCP 通道当前没有 TLS、应用层鉴权或消息签名,必须运行在隔离、受控的工业网络中;
  • 本地用户登录只保护 WPF 操作入口,不能识别 PLC 连接方身份;
  • 协议会记录原始通信内容用于排障,日志权限和保留周期必须按生产数据管理;
  • 后续应收敛监听地址、限制来源,并在协议兼容允许时增加网关认证或加密层。

数据一致性与上传队列

  • 绑定、生产记录和上传队列通过多个仓储分别保存,不在同一个数据库事务中;
  • 中间步骤失败可能出现“绑定成功但追溯或上传项未写入”的部分完成状态;
  • 队列没有处理租约,多个应用实例同时运行时可能重复处理同一记录;
  • 外部上报成功但本地状态更新失败时,后续重试可能形成重复提交;
  • 当前没有专门的死信状态、人工重放界面或完整队列服务测试。

日志与配置安全

  • 外部 MES 适配器当前可能记录完整接口地址、请求载荷和响应正文,其中可能包含产品身份和业务数据;
  • 外部接口凭据由现场配置提供,配置文件和安装备份需要限制访问;
  • 应优先实施结构化脱敏,只保留调用标识、耗时、状态和经过筛选的错误摘要;
  • 升级时保留旧配置,需要校验新增字段、默认值和密钥轮换状态。

认证与权限

  • 当前管理员标识不等同于细粒度 RBAC,模块、动作和数据范围尚未形成统一策略;
  • 密码最小规则较基础,登录失败锁定、会话超时和强制轮换仍需加强;
  • 初始账户、管理员授予和离职停用需要纳入现场交付与审计清单。

运行、验证与恢复

  • 系统依赖 Windows、SQLite、现场 PLC 网络和外部 MES,跨平台部署不在当前范围;
  • 自动化测试不能替代真实 PLC、异常断网、外部接口限流和硬件在环验证;
  • 源码仓库目前没有自动构建、测试和安装包验证工作流;
  • 备份位于本机且按时间保留,异机复制、恢复时间和恢复点验收仍待固化;
  • SQLite 适合当前单机部署,但需要持续监控文件增长、锁竞争、磁盘空间和备份可恢复性。

后续路线

  1. 将绑定、生产记录和上传队列改造为同一事务内写入的事务型 Outbox;
  2. 为上传队列增加处理租约、幂等键、死信状态、人工重放和积压监控;
  3. 对外部 MES 请求、响应和终端通信日志实施字段级脱敏与分级保留;
  4. 把接口凭据迁移到受控密钥存储,并建立配置 schema 与轮换流程;
  5. 收敛 TCP 监听与来源范围,评估网关认证、TLS 或消息签名的兼容方案;
  6. 将本地管理员模型扩展为可审计的 RBAC,并增加登录限流、锁定和密码策略;
  7. 建立包含 Release 构建、192 项测试、静态分析和安装包验证的 CI;
  8. 用发布 manifest 关联源码、安装包、数据库迁移、配置版本和验收记录;
  9. 增加 PLC 仿真、断网重试、重复消息和硬件在环分层回归;
  10. 建立异机备份、定期恢复演练、磁盘告警和版本回退流程。

项目复盘

这个项目的核心价值不是把 TCP、SQLite 和 HTTP 拼接起来,而是为每条现场消息建立一条可解释的数据路径:

  • 设备请求先被正确分帧和顺序处理;
  • RFID 与产品身份通过有效绑定建立生命周期;
  • 工序结果先成为本地生产事实,再进入外部同步链路;
  • 重复请求通过幂等规则处理,换码和解绑保留历史;
  • 外部 MES 故障不会直接抹掉本地追溯证据;
  • 操作界面提供监控和恢复入口,但不替代数据库事实;
  • 自动化测试验证软件契约,现场验收验证真实系统边界。

生产使用证明了这套链路能够承担实际业务,但也让一致性、安全和运维债务更加具体。下一阶段的重点应是事务型 Outbox、日志脱敏、通信边界、持续集成和可恢复发布,而不是继续堆叠命令数量。