这是使用PyQT5作为UI的首次提交,将后端和UI合并到1个工程中,统一使用了Python,没有使用JS和HTML
This commit is contained in:
567
架构设计文档.md
Normal file
567
架构设计文档.md
Normal file
@@ -0,0 +1,567 @@
|
||||
# 数字孪生跌落机监控系统 (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
|
||||
**维护者**: 系统架构团队
|
||||
Reference in New Issue
Block a user