568 lines
19 KiB
Markdown
568 lines
19 KiB
Markdown
# 数字孪生跌落机监控系统 (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)
|
||
- 进程生命周期管理
|
||
|
||
**关键组件**:
|
||
```python
|
||
├─ Flask HTTP Server (端口5050) - REST API接口
|
||
├─ WebSocket Server (端口5080) - 实时数据推送
|
||
├─ Modbus Server (独立进程, 端口5020) - PLC网关
|
||
├─ LMIS上传服务 (线程池) - 异步上传测试结果
|
||
└─ 本地数据存储线程 - SQLite批量写入
|
||
```
|
||
|
||
---
|
||
|
||
### 3.2 业务服务层 (dtMachineService.py)
|
||
|
||
**核心类**: `DtmMachineService`
|
||
|
||
**主要功能**:
|
||
1. **设备管理**: 管理多台跌落机及其工位状态
|
||
2. **数据轮询**: 每个设备独立线程轮询PLC数据 (0.6秒间隔)
|
||
3. **状态同步**: PLC状态与工位状态双向同步
|
||
4. **测试结果处理**: 解析PLC测试结果并触发存储/上传
|
||
|
||
**关键方法**:
|
||
```python
|
||
- 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帧格式 + 自定义校验和
|
||
|
||
**核心方法**:
|
||
```python
|
||
- 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`)
|
||
|
||
**优化设计**:
|
||
```python
|
||
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,屏蔽底层通信细节
|
||
|
||
**寄存器映射**:
|
||
```python
|
||
# 三菱PLC地址转换
|
||
'D0160' → Modbus地址: 160
|
||
'M0016' → Modbus Coil地址: 16
|
||
|
||
# 信捷PLC地址转换
|
||
'HD0160' → Modbus地址: 41088 + 160
|
||
```
|
||
|
||
**关键功能**:
|
||
```python
|
||
- 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存储
|
||
|
||
**线程设计**:
|
||
```python
|
||
# 主线程
|
||
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`表
|
||
|
||
**关键字段**:
|
||
```python
|
||
{
|
||
'SN': '设备序列号',
|
||
'com': 'com2', # 串口号
|
||
'plcAddress': '01', # PLC站地址
|
||
'type': 'xinjie', # PLC类型
|
||
'baudrate': {...}, # 波特率配置
|
||
'register01': {...}, # 工位1寄存器配置
|
||
'register02': {...}, # 工位2寄存器配置
|
||
# ...
|
||
}
|
||
```
|
||
|
||
### 7.2 寄存器映射示例
|
||
|
||
```python
|
||
drop_register = {
|
||
'01': {
|
||
"height": "D0160", # 跌落高度
|
||
"cycles": "D0200", # 设定次数
|
||
"cyclesFinished": "D0202", # 完成次数
|
||
"start": "M0016", # 启动位
|
||
"stop": "M0008", # 停止位
|
||
"runStatus": "M0010", # 运行状态
|
||
"stationResultCounter": "D0300", # 结果计数器
|
||
"dutBasic": "D0400" # 样品信息起始地址
|
||
}
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 8. 异常处理机制
|
||
|
||
### 8.1 PLC通信异常
|
||
|
||
**策略**: 捕获异常 + 连接状态缓存
|
||
|
||
```python
|
||
# 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秒
|
||
|
||
```python
|
||
# 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. 性能优化点
|
||
|
||
1. **批量数据库写入** - 2秒间隔批量提交
|
||
2. **连接状态缓存** - 避免频繁读取PLC
|
||
3. **HTTP连接池** - requests.Session复用TCP连接
|
||
4. **WebSocket消息合并** - 相同设备的多次更新合并为一次
|
||
5. **线程池上传** - 20并发上传LMIS
|
||
|
||
---
|
||
|
||
## 10. 扩展性设计
|
||
|
||
### 10.1 多PLC协议支持
|
||
|
||
**抽象接口**:
|
||
```python
|
||
# 统一的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 多设备并发
|
||
|
||
**设计**: 每台设备独立线程轮询
|
||
|
||
```python
|
||
for machine in machines:
|
||
thread = threading.Thread(target=machine_poll, args=[machine])
|
||
thread.start()
|
||
```
|
||
|
||
---
|
||
|
||
## 11. 数据模型
|
||
|
||
### 11.1 核心数据结构
|
||
|
||
#### 测试样品 (DUT)
|
||
```python
|
||
{
|
||
'SN': '样品序列号',
|
||
'name': '样品名称',
|
||
'project': '项目代码',
|
||
'phase': '项目阶段',
|
||
'weeks': '测试周次',
|
||
'workOrder': '工单号',
|
||
'testReqList': [...] # 测试项列表
|
||
}
|
||
```
|
||
|
||
#### 测试结果 (TestReqResult)
|
||
```python
|
||
{
|
||
'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-工位状态映射**:
|
||
```python
|
||
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
|
||
**维护者**: 系统架构团队
|