19 KiB
19 KiB
数字孪生跌落机监控系统 (DTMGT) - 架构设计文档
1. 系统概述
本系统是一个工业自动化跌落试验管理系统,实现了PC上位机与PLC控制器之间的实时通信、数据采集、本地存储和远程上传功能。
2. 系统架构分层
2.1 三层架构设计
┌─────────────────────────────────────────────────────────────┐
│ 前端展示层 (Web UI) │
│ 技术栈: HTML + JavaScript + Eel.js / Socket.io │
│ 功能: 设备监控、参数配置、测试管理、数据展示 │
└─────────────────────────────────────────────────────────────┘
↕ WebSocket / HTTP
┌─────────────────────────────────────────────────────────────┐
│ 应用服务层 (Python Backend) │
│ 核心服务: │
│ ├─ Flask HTTP Server (api_route.py) - REST API服务 │
│ ├─ WebSocket Server - 实时数据推送 │
│ ├─ DtmMachineService - 核心业务逻辑 │
│ ├─ LMISUploadSystem - LMIS数据上传服务 │
│ └─ SQLite数据库访问层 │
└─────────────────────────────────────────────────────────────┘
↕ Modbus TCP
┌─────────────────────────────────────────────────────────────┐
│ 通信网关层 (Gateway Process) │
│ 独立进程: ModbusServer (modbus_server.py) │
│ 功能: │
│ ├─ Modbus TCP Server (对上位机提供服务) │
│ └─ Modbus RTU Client (与PLC串口通信) │
└─────────────────────────────────────────────────────────────┘
↕ RS-485/RS-232
┌─────────────────────────────────────────────────────────────┐
│ 设备控制层 (PLC) │
│ 支持协议: │
│ ├─ 三菱MHI协议 (mhi_plc.py) │
│ └─ 信捷Modbus协议 (modbus_plc.py) │
└─────────────────────────────────────────────────────────────┘
3. 核心模块详解
3.1 主程序入口 (dtmgtApp.py)
职责:
- 系统初始化与多线程管理
- 启动各服务组件 (Flask, WebSocket, Modbus Server)
- 进程生命周期管理
关键组件:
├─ Flask HTTP Server (端口5050) - REST API接口
├─ WebSocket Server (端口5080) - 实时数据推送
├─ Modbus Server (独立进程, 端口5020) - PLC网关
├─ LMIS上传服务 (线程池) - 异步上传测试结果
└─ 本地数据存储线程 - SQLite批量写入
3.2 业务服务层 (dtMachineService.py)
核心类: DtmMachineService
主要功能:
- 设备管理: 管理多台跌落机及其工位状态
- 数据轮询: 每个设备独立线程轮询PLC数据 (0.6秒间隔)
- 状态同步: PLC状态与工位状态双向同步
- 测试结果处理: 解析PLC测试结果并触发存储/上传
关键方法:
- machine_poll() # PLC数据轮询主循环
- handle_status_sync_with_plc() # PLC状态同步到工位
- machine_poll_test_result_bulk() # 批量读取测试结果
- handle_test_result_registers_value() # 解析测试结果
- dtStationAttachDut() # 工位绑定测试样品
设计模式: 观察者模式 (ObservableDict监听数据变化)
3.3 通信网关层 (modbus_server.py)
架构特点: 独立进程部署,支持跨机器扩展
核心类: ModbusServer (继承自multiprocessing.Process)
工作流程:
1. 主服务启动 → 创建ModbusServer进程
2. ModbusServer初始化:
├─ 根据机台配置创建PLC设备实例 (MitsubishiPLC/ModbusPLC)
├─ 为每个机台创建CustomDataBlock
└─ 启动Modbus TCP Server (异步事件循环)
3. 主服务 → Modbus TCP Client连接 → Modbus Server
4. CustomDataBlock拦截读写请求 → 转发到物理PLC
CustomDataBlock设计:
- 重写
getValues()- 读取时触发PLC通信 - 重写
setValues()- 写入时触发PLC通信 - 线程锁保护串口访问 (
plc_comm_lock)
3.4 PLC通信抽象层
3.4.1 三菱MHI协议 (mhi_plc.py)
协议特点: ASCII帧格式 + 自定义校验和
核心方法:
- plc_read_frame() # 构建读请求帧
- plc_write_frame() # 构建写请求帧
- frame_is_valid() # 校验响应帧
- plc_read_words() # 读寄存器
- plc_write_words() # 写寄存器
3.4.2 信捷Modbus协议 (modbus_plc.py)
特点:
- 使用pymodbus库标准Modbus RTU协议
- 后台线程轮询PLC连接状态 (500ms间隔)
- 连接状态缓存机制 (
_connect_state_cache)
优化设计:
def _poll_connect_state():
"""后台线程定期更新连接状态缓存"""
while not self._stop_event.is_set():
# 读取D10寄存器的5个值 [连接状态, 工位1, 工位2, 工位3, 工位4]
result = self.read_holding_registers(10, 5, slave)
if success:
self._connect_state_cache = result['data']
3.5 Modbus虚拟设备层 (dtMachine_modbus.py)
核心类: ModbusDtMachine
职责: 封装Modbus TCP Client,屏蔽底层通信细节
寄存器映射:
# 三菱PLC地址转换
'D0160' → Modbus地址: 160
'M0016' → Modbus Coil地址: 16
# 信捷PLC地址转换
'HD0160' → Modbus地址: 41088 + 160
关键功能:
- set_station_dropheight() # 设置跌落高度
- set_station_cycles() # 设置跌落次数
- set_station_dutInfo() # 传输样品信息(最多4个)
- read_station_dutInfo() # 读取样品信息
- read_station_result_counter() # 读取结果计数器
- read_station_cyclesFinishedBulk() # 批量读取测试结果
3.6 数据存储与上传
3.6.1 本地SQLite存储
线程设计:
# 主线程
result_to_localDB_queue.put(test_result) # 非阻塞入队
# 专用写入线程 (run_insert_result)
while True:
records = []
while not queue.empty():
records.append(queue.get())
if records:
dtMachineService.insertTableRecords('TestReq', records) # 批量写入
time.sleep(2) # 2秒间隔
3.6.2 LMIS远程上传
架构: 线程池 + 重试队列
核心类: LMISUploadSystem
特性:
- 20线程并发上传
- 失败自动重试 (最多5次, 间隔60秒)
- 优先级队列管理重试任务
- HTTP连接池复用 (
requests.Session)
工作流程:
1. cb_insert_result() 触发
2. 数据进入上传系统 → executor.submit()
3. 成功 → 结束
4. 失败 → 放入retry_queue (优先级队列)
5. 后台监控线程检测到达重试时间 → 重新提交
4. 数据流图
4.1 测试结果采集流程
┌──────────┐ ┌──────────────┐ ┌──────────────┐
│ PLC │────>│ Modbus Server│────>│dtMachineService│
│ (跌落机) │ │ (网关进程) │ │ (轮询线程) │
└──────────┘ └──────────────┘ └──────┬───────┘
│
↓
┌─────────────────────┴─────────────────┐
│ handle_test_result_registers_value() │
│ 解析寄存器 → PLCTestReqResultData │
└─────────────────┬─────────────────────┘
│
┌─────────────────┴─────────────────┐
│ cb_insert_result() 回调 │
└─────┬───────────────────┬─────────┘
│ │
┌─────────↓──────┐ ┌───────↓─────────┐
│ SQLite队列入库 │ │ LMIS上传队列 │
│ (2秒批量写) │ │ (线程池异步) │
└────────────────┘ └─────────────────┘
4.2 前端实时数据推送
┌──────────────┐ ┌──────────────┐ ┌──────────┐
│machine_poll()│────>│cb_machine_data│────>│WebSocket │
│ (PLC轮询) │ │ _change() │ │消息队列 │
└──────────────┘ └──────────────┘ └────┬─────┘
│
┌──────↓──────┐
│ws_dispatcher│
│ (消息合并) │
└──────┬──────┘
│
┌──────↓──────┐
│ Web前端 │
│ (实时刷新) │
└─────────────┘
5. 关键设计决策
5.1 网关独立进程设计
优点:
- 支持跨机器部署
- 隔离PLC通信故障
- 便于横向扩展
缺点:
- 进程间通信开销
- 调试复杂度增加
5.2 轮询 vs 事件驱动
当前采用: 定时轮询 (0.6秒)
原因:
- PLC不支持主动推送
- 简化并发控制
- 满足实时性要求
5.3 双缓冲结果读取
设计: PLC维护A/B两块结果缓冲区
优势:
- 避免读写冲突
- 提高数据一致性
实现: 通过result_index_read切换 (0~9循环)
6. 并发控制策略
6.1 线程锁使用
| 位置 | 锁类型 | 保护资源 |
|---|---|---|
| modbus_plc.py | threading.Lock | 串口读写操作 |
| modbus_server.py | threading.Lock | PLC通信访问 |
| dtMachineService.py | busy_flag | 机器操作互斥 |
6.2 队列通信
| 队列 | 生产者 | 消费者 | 用途 |
|---|---|---|---|
| result_to_localDB_queue | cb_insert_result | run_insert_result | 本地存储 |
| ws_message_send_queue | 数据回调 | ws_dispatcher | WebSocket推送 |
| LMISUploadSystem.retry_queue | 上传失败 | 重试监控线程 | 延迟重试 |
7. 配置管理
7.1 PLC通信配置
存储位置: SQLite dtMachine表
关键字段:
{
'SN': '设备序列号',
'com': 'com2', # 串口号
'plcAddress': '01', # PLC站地址
'type': 'xinjie', # PLC类型
'baudrate': {...}, # 波特率配置
'register01': {...}, # 工位1寄存器配置
'register02': {...}, # 工位2寄存器配置
# ...
}
7.2 寄存器映射示例
drop_register = {
'01': {
"height": "D0160", # 跌落高度
"cycles": "D0200", # 设定次数
"cyclesFinished": "D0202", # 完成次数
"start": "M0016", # 启动位
"stop": "M0008", # 停止位
"runStatus": "M0010", # 运行状态
"stationResultCounter": "D0300", # 结果计数器
"dutBasic": "D0400" # 样品信息起始地址
}
}
8. 异常处理机制
8.1 PLC通信异常
策略: 捕获异常 + 连接状态缓存
# modbus_plc.py
try:
result = self.client.read_holding_registers(...)
if result.isError():
return {"status": "error"}
except ModbusException:
if self.reconnect(): # 尝试重连
return self.read_holding_registers(...) # 重试
8.2 数据上传异常
策略: 最多重试5次, 每次间隔60秒
# LMISUploadSystem
retry_count = 0
while retry_count < 5:
try:
response = requests.post(url, ...)
if success:
break
except Exception:
retry_count += 1
time.sleep(60)
9. 性能优化点
- 批量数据库写入 - 2秒间隔批量提交
- 连接状态缓存 - 避免频繁读取PLC
- HTTP连接池 - requests.Session复用TCP连接
- WebSocket消息合并 - 相同设备的多次更新合并为一次
- 线程池上传 - 20并发上传LMIS
10. 扩展性设计
10.1 多PLC协议支持
抽象接口:
# 统一的PLC接口
class PLCInterface:
def plc_read_words(plc_no, address, length)
def plc_write_words(plc_no, address, length, data)
def plc_read_bits(plc_no, address, length)
def plc_write_bit(plc_no, address, data)
实现:
MitsubishiPLC- 三菱MHI协议ModbusPLC- 标准Modbus RTU协议
10.2 多设备并发
设计: 每台设备独立线程轮询
for machine in machines:
thread = threading.Thread(target=machine_poll, args=[machine])
thread.start()
11. 数据模型
11.1 核心数据结构
测试样品 (DUT)
{
'SN': '样品序列号',
'name': '样品名称',
'project': '项目代码',
'phase': '项目阶段',
'weeks': '测试周次',
'workOrder': '工单号',
'testReqList': [...] # 测试项列表
}
测试结果 (TestReqResult)
{
'SN': '样品SN',
'machine_sn': '设备SN',
'station_no': '工位号',
'dropDirection': '跌落方向',
'dropHeight_int': '跌落高度(mm)',
'dropSpeed': '跌落速度',
'dutDropCycles': '样品累计次数',
'itemCurrentCycles': '方向累计次数',
'stationDropCycles': '工位累计次数',
'startTime': '开始时间',
'endTime': '结束时间'
}
11.2 状态机
工位状态流转:
idle → dutAttached → running → pause → running → finished → idle
↓ ↑ ↓
(绑定样品) (恢复试验) (暂停)
PLC-工位状态映射:
TRANSITION_TABLE = {
(0, 'idle'): ('none', None, False), # 一致
(0, 'running'): ('detach', 'stop_by_plc', False), # PLC停止
(1, 'idle'): ('attach_dut', 'resync_with_plc_run', True), # 重新同步
(1, 'running'): ('none', None, False), # 一致
(1, 'pause'): ('attach', 'resume_by_plc', False), # PLC恢复
(2, 'pause'): ('none', None, False), # 一致
(3, 'running'): ('finish', None, False), # 试验完成
}
12. 技术栈总结
| 层级 | 技术 | 用途 |
|---|---|---|
| 前端 | HTML + JavaScript + Eel.js | 桌面化Web界面 |
| 通信 | WebSocket + Socket.io | 实时数据推送 |
| 后端 | Flask + Flask-CORS | REST API服务 |
| 数据库 | SQLite | 本地数据存储 |
| 工业通信 | pymodbus + pyserial | Modbus协议 |
| 并发 | threading + multiprocessing | 多线程/多进程 |
| 异步 | asyncio + gevent | 异步IO |
| HTTP | requests (Session) | HTTP客户端 |
13. 部署架构
13.1 单机部署
┌────────────────────────────────────────┐
│ Windows PC (上位机) │
│ ┌────────────────────────────────┐ │
│ │ dtmgtApp.py (主进程) │ │
│ │ ├─ Flask (5050) │ │
│ │ ├─ WebSocket (5080) │ │
│ │ └─ Eel WebView │ │
│ └────────┬───────────────────────┘ │
│ │ │
│ ┌────────↓───────────────────────┐ │
│ │ ModbusServer (子进程) │ │
│ │ └─ Modbus TCP (5020) │ │
│ └────────┬───────────────────────┘ │
│ │ │
│ ┌────↓────┐ │
│ │ COM口 │ │
└──────┴─────────┴──────────────────────┘
│
RS-485/RS-232
│
┌────↓────┐
│ PLC │
└─────────┘
13.2 分布式部署 (未来扩展)
┌────────────┐ ┌────────────┐
│ 上位机1 │ │ 上位机2 │
│ dtmgtApp │ │ dtmgtApp │
└─────┬──────┘ └─────┬──────┘
│ │
└─────────────┬───────────────────┘
│ HTTP/WebSocket
┌─────↓──────┐
│ 中心服务器 │
│ (统一管理) │
└─────┬──────┘
│
┌───────────┼───────────┐
│ │ │
┌────↓────┐ ┌───↓────┐ ┌───↓────┐
│Gateway1 │ │Gateway2│ │Gateway3│
└────┬────┘ └───┬────┘ └───┬────┘
│ │ │
PLC1 PLC2 PLC3
文档版本: v1.0 最后更新: 2025-11-19 维护者: 系统架构团队