# 数字孪生跌落机监控系统 (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 **维护者**: 系统架构团队