这是使用PyQT5作为UI的首次提交,将后端和UI合并到1个工程中,统一使用了Python,没有使用JS和HTML

This commit is contained in:
2025-12-14 12:13:19 +08:00
commit 872b181703
122 changed files with 70944 additions and 0 deletions

567
架构设计文档.md Normal file
View 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
**维护者**: 系统架构团队