# MES 注塑机数采对接需求与设计 > 状态:待评审(评审通过后开始编码) > 编写日期:2026-10-20 > 适用数据库:`mom-kyzn` > 涉及模块:`yudao-module-mes` ## 1. 背景与目标 客户注塑机通过弘讯采集网关,将运行状态、周期过程、工艺参数、参数变更和报警以 MQTT 报文推送到 EMQX。当前 MES 没有任何 MQTT 接入代码,需要完成“订阅 → 解析 → 落库 → 查询/展示”的全链路对接,并把采集数据与 MES 设备台账、生产任务/工单关联起来。 本期目标(已与用户确认): 1. **全量接入**:realtime(实时状态)、spc(周期过程)、tech(工艺参数)、opLog(参数变更)、报警五类数据全部落库。 2. **设备绑定**:在 `mes_dv_machinery` 设备台账上增加“数采设备编号”,`devId`(如 `C01`)直接匹配台账。 3. **工单联动**:采集数据按“设备所属工作站上正在运行的生产任务”自动关联 `taskId / workOrderId`,关联不到时留空。 4. **分级保留**:原始报文 7 天、实时历史 30 天、周期/工艺/变更/报警长期保留。 不在本期范围:前端页面代码(按项目规则只输出联调方案)、设备反向控制(下行指令)、多实例共享订阅。 ## 2. 接入信息 | 项目 | 值 | |------|----| | EMQX 地址 | `36.213.90.123:1883`(MQTT),`18083`(Dashboard/HTTP API) | | 账号 | `admin / public`(Dashboard 与 MQTT 共用) | | 协议 | MQTT 3.1.1,QoS 0 | | 主题前缀 | `/YLCY/IMM`(可配置) | | 设备编号 | `C01` ~ `C08`(`devId`) | | 订阅通配 | `{prefix}/+/+`,即 `/YLCY/IMM/+/+` | | 客户端 ID 示例 | `/YLCY/IMM/C03/id` | 主题约定(来自采集文档): | 数据类型 | 主题 | 说明 | |----------|------|------| | realtime | `{prefix}/{devId}/realtime` | 实时运行状态、温度、油温 | | spc | `{prefix}/{devId}/spc` | 每个注塑周期的过程数据 | | tech | `{prefix}/{devId}/tech` | 工艺设定参数全量快照 | | opLog | `{prefix}/{devId}/opLog` | 单个参数修改前后值 | | 报警 | 文档未给出主题名 | 通过报文 `topic` 字段或 `Data.wmId` 识别;订阅通配兜底 | > 说明:报警主题名在文档截图中缺失,因报文自带 `topic` 字段且 `Data` 中 `wmId/wmMsg/wmTime` 特征唯一,代码按报文特征识别,主题后缀仅作为配置项兜底,不阻塞接入。 ## 3. 报文规范 ### 3.1 公共信封 ```json { "clientId": "/YLCY/IMM/C03/id", "devId": "C03", "topic": "realtime", "sendTime": "2026-09-24 11:37:00", "sendStamp": 1790221000000, "time": "2026-09-24 11:37:00", "timestamp": 1790221000000, "Data": { } } ``` - 数据时间优先取 `time`(`yyyy-MM-dd HH:mm:ss`),缺失时回退 `timestamp`。 - `Data` 可能为对象;若为字符串,二次解析为对象。 - 数据类型优先取 `topic` 字段,其次按主题后缀,最后按 `Data` 特征识别。 ### 3.2 realtime(实时状态) `Data` 示例字段: | 编码 | 含义 | 备注 | |------|------|------| | `STS` | 状态 | 1 待机,2 生产 | | `OPM` | 运行模式 | 0 手动,1 半自动,2 电眼自动,3 时间自动,4 调模使用 | | `OT` | 油温 | | | `T1..Tn` | 各段温度 | 段数按实际为准,动态解析 | | `ASTS` | 附加状态 | 原样保存 | ### 3.3 spc(周期过程数据) | 编码 | 含义 | |------|------| | `CYCN` | 周期序号(部分系列独立计数,增量一致) | | `ECYCT` | 周期时间 | | `EISS` | 射出起点 | | `EIVM` | 最大射速 | | `EIPM` | 最大射压 | | `ESIPT` | 转保压时间 | | `ESIPP` | 转保压压力 | | `ESIPS` | 转保压位置 | | `EIPT` | 射出时间 | | `EIPSE` | 射出终点位置 | | `EPLST` | 储料时间 | | `EPLSSE` | 储料终点 | | `EPLSPM` | 最大储料压力 | | `ET1..ETn` | 生产温度(各段) | ### 3.4 tech(工艺参数) 按文档第 3 节全量字段保存(TS/IP/IV/IS/IT/ITUSE/ISUSE/SIPT/SIPS/SIPP/PP/PV/PT/SBS/SBT/PLLT/PLV/PLP/PLBP/PLS/SBV/SBP/SBS/SBT/CT/MCV/MCP/MCS/MOV/MOP/MOS/EFDT/EFV/EFP/EFS/EBDT/EBV/EBP/EBS/CP1M~CP4M/CPI1S~CPI4S/CPI1P~CPI4P/CPI1V~CPI4V/CPI1T~CPI4T/CPO1S~CPO4S/CPO1P~CPO4P/CPO1V~CPO4V/CPO1T~CPO4T/CFP/CFV/CFS/CFT/CBP/CBV/CBS/CBT/CBDT)。字段数量多且可能扩展,**不建宽表**,整包 JSON 快照保存,前端按 JSON 渲染。 ### 3.5 opLog(参数变更) ```json { "devId": "5", "time": "2022-12-09 12:09:48", "Data": { "varId": "SIPS", "value": 37.5, "lastValue": 37, "modifyTime": "2022-12-09 12:09:46" } } ``` ### 3.6 报警 ```json { "devId": "6", "Data": { "wmId": 2, "wmMsg": "安全门未关", "wmTime": "2022-12-09 16:42:22" } } ``` - `wmId = 2`:报警产生 → 新增一条“未消除”记录。 - `wmId = 0`:报警消失 → 把该设备所有“未消除”记录置为“已消除”,写入 `clearTime`。 ## 4. 数据模型 > 所有新表沿用项目 `BaseDO` 字段:`creator/create_time/updater/update_time/deleted`。高频日志表按保留策略物理清理。 ### 4.1 设备台账扩展 ```sql ALTER TABLE mes_dv_machinery ADD COLUMN collect_device_code VARCHAR(64) NULL COMMENT '数采设备编号(MQTT devId,如 C01)' AFTER workstation_id, ADD COLUMN collect_enabled BIT(1) NOT NULL DEFAULT b'0' COMMENT '是否启用数采' AFTER collect_device_code; ALTER TABLE mes_dv_machinery ADD UNIQUE INDEX uk_collect_device_code (collect_device_code); ``` - `collect_device_code` 为空表示未绑定数采。 - 唯一索引允许多个 `NULL`,不会影响存量设备。 - 关联任务时使用台账已有的 `workstation_id`。 ### 4.2 新建表 | 表名 | 用途 | 保留 | |------|------|------| | `mes_collect_realtime` | 每设备最新实时状态(UPSERT) | 常驻 | | `mes_collect_realtime_history` | 实时状态历史 | 30 天 | | `mes_collect_cycle` | 周期过程数据 | 长期 | | `mes_collect_tech` | 工艺参数最新快照(UPSERT) | 常驻 | | `mes_collect_operate_log` | 参数变更日志 | 长期 | | `mes_collect_alarm` | 报警记录 | 长期 | | `mes_collect_message` | 原始报文(排障、补数依据) | 7 天 | 关键字段: - `mes_collect_realtime`:`device_code`(唯一)、`machinery_id`、`task_id`、`work_order_id`、`sts`、`opm`、`asts`、`ot`、`temps` JSON、`data_json`、`collect_time`。 - `mes_collect_realtime_history`:字段同上,唯一键 `(device_code, collect_time)`,索引 `(device_code, collect_time)`、`(work_order_id, collect_time)`。 - `mes_collect_cycle`:`device_code`、`cycle_no`、`collect_time`、`ecyct/eiss/eivm/eipm/esipt/esipp/esips/eipt/eipse/eplst/eplsse/eplspm`、`et_temps` JSON、`data_json`;唯一键 `(device_code, cycle_no, collect_time)`。 - `mes_collect_tech`:`device_code`(唯一)、`task_id`、`work_order_id`、`collect_time`、`data_json`。 - `mes_collect_operate_log`:`device_code`、`var_id`、`old_value`、`new_value`、`modify_time`、`collect_time`、`data_json`;索引 `(device_code, modify_time)`。 - `mes_collect_alarm`:`device_code`、`wm_id`、`wm_msg`、`wm_time`、`status`(0 未消除/1 已消除)、`clear_time`、`data_json`;索引 `(device_code, status)`、`(wm_time)`。 - `mes_collect_message`:`device_code`、`machinery_id`、`mqtt_topic`、`msg_type`、`event_time`、`raw_payload` LONGTEXT、`process_status`(0 成功/1 失败)、`error_msg`、`receive_time`。 ### 4.3 落库规则 - `realtime`:更新 `mes_collect_realtime` 快照 + 写 `mes_collect_realtime_history`(唯一键去重)。 - `spc`:写 `mes_collect_cycle`(唯一键去重,`INSERT IGNORE`)。 - `tech`:整包 JSON UPSERT `mes_collect_tech`;不重复生成变更日志,变更以设备上报的 `opLog` 为准。 - `opLog`:逐条写 `mes_collect_operate_log`。 - 报警:按 `wmId` 规则新增/清除。 - 所有报文先写 `mes_collect_message`,解析失败也保留原文并记录 `error_msg`。 - 未绑定台账的 `devId`:结构化数据照常入库(`machinery_id/task_id/work_order_id` 为空),原始报文保留,并以限流日志提示补绑台账,避免设备已上线但台账未维护时丢数据。 ## 5. 工单自动关联 ``` devId → mes_dv_machinery.collect_device_code → machinery.workstation_id → mes_pro_task(workstation_id 匹配、deleted=0、status IN (1 生产中, 2 部分完成, 9 已停工)) → 取 last_feedback_time / id 最新的一条 → task_id + work_order_id ``` - 结果缓存 30 秒(按 `workstationId`),避免每条报文查库。 - 关联不到时 `task_id / work_order_id` 为空,不影响落库。 - 任务切换后最多 30 秒延迟生效。 ## 6. 后端实现设计 ### 6.1 代码位置 ``` yudao-module-mes/ ├── controller/admin/collect/ # 查询接口 + VO ├── service/collect/ # 查询、解析、落库、关联 │ ├── MesCollectQueryService │ ├── MesCollectDataService │ ├── MesCollectMessageDispatcher │ ├── MesCollectDataParser │ ├── MesCollectTaskResolver │ └── MesCollectCleanupJob(@Scheduled) ├── framework/collect/mqtt/MesCollectMqttClient # Paho 连接、订阅、重连 ├── dal/dataobject/collect/ # DO └── dal/mysql/collect/ # Mapper ``` - MQTT 客户端使用项目依赖管理里已有的 `org.eclipse.paho.client.mqttv3:1.2.5`,在 `yudao-module-mes/pom.xml` 引入即可。 - Paho 回调只做解析入队,落库放到单线程有界队列执行,避免阻塞心跳。 - 开启 `automaticReconnect`,客户端 ID 使用 `{prefix}-{hostname}-{random}`;**生产单实例部署**,多实例会重复消费(后续可改共享订阅)。 - 通过 `yudao.mes.collect.mqtt.enabled` 控制是否连接,默认关闭,按环境开启。 ### 6.2 配置项(`application-kyzn.yaml`,并同步 `local/test`) ```yaml yudao: mes: collect: mqtt: enabled: true url: tcp://36.213.90.123:1883 username: admin password: public client-id-prefix: mes-collect topic-prefix: /YLCY/IMM qos: 0 keep-alive: 65 connection-timeout: 10 alarm-topic-suffix: wm retention: raw-days: 7 realtime-days: 30 ``` ### 6.3 清理任务 - 每日 03:30 执行。 - 物理删除 `mes_collect_message` 中 `receive_time < now - 7 天`、`mes_collect_realtime_history` 中 `collect_time < now - 30 天` 的数据。 - 单次删除限制批量,避免长事务锁表。 ## 7. 后端接口 统一前缀 `/mes/collect`,全部返回项目标准 `CommonResult`。 | 方法 | 路径 | 说明 | 权限 | |------|------|------|------| | GET | `/device/list` | 设备数采状态列表(台账 + 最新状态 + 当前任务) | `mes:collect:query` | | GET | `/realtime/get` | 指定设备最新实时状态 | `mes:collect:query` | | GET | `/realtime/history-page` | 实时状态历史分页 | `mes:collect:query` | | GET | `/cycle/page` | 周期过程数据分页 | `mes:collect:query` | | GET | `/cycle/get` | 周期明细 | `mes:collect:query` | | GET | `/tech/get` | 指定设备最新工艺参数快照 | `mes:collect:query` | | GET | `/operate-log/page` | 参数变更分页 | `mes:collect:query` | | GET | `/alarm/page` | 报警分页 | `mes:collect:query` | | PUT | `/alarm/ack` | 手工确认报警 | `mes:collect-alarm:ack` | 设备绑定复用现有设备台账新增/更新接口,新增字段 `collectDeviceCode`、`collectEnabled`;台账分页支持按 `collectDeviceCode` 过滤。 ## 8. 权限与菜单 - 新增权限码:`mes:collect:query`、`mes:collect-alarm:ack`。 - 菜单:设备管理(5300)下新增“数采监控”(`collect/monitor`)。 - 同步写入 `docs/sql/config_export_all_*.sql` 与数据库初始化脚本,避免新库缺菜单/权限。 - 按“权限码三方对齐”规则,菜单、角色授权、后端注解三处同时落地。 ## 9. 部署与验证 1. 执行 `docs/sql/mes_collect_20261020.sql`(本地 `mom-kyzn` 先执行验证;生产由运维执行)。 2. 在设备台账填写 `collect_device_code` 并启用数采。 3. 启动后端,确认日志出现 MQTT 连接成功、订阅成功。 4. 观察 `mes_collect_message` 是否有各类型报文入库。 5. 校验 `mes_collect_realtime / cycle / tech / operate_log / alarm` 数据与 EMQX 报文一致。 6. 触发一次参数修改与报警,确认变更日志和报警状态流转正确。 7. 验证工单关联:工作站存在生产中任务时,新数据的 `task_id/work_order_id` 不为空。 ## 10. 风险与说明 - **报警主题名缺失**:按报文特征识别,已兜底;如后续确认主题名只需改配置。 - **CYCN 重置**:周期序号可能随设备重启重置,去重键包含采集时间,避免误合并。 - **多实例部署**:当前为单实例消费,多实例会产生重复数据;后续可切换 EMQX 共享订阅。 - **时钟偏差**:设备时间与服务器时间可能不一致,事件时间以报文 `time` 为准,另存 `receive_time`。 - **数据量**:按 8 台设备、30 秒一帧估算,实时历史约 70 万行/月;如后续提高频率,需评估分区或改时序库。 - **不修改前端代码**:按项目规则另行输出前端联调方案。