Files
dtm-py-all/架构设计文档.md

19 KiB
Raw Permalink Blame History

数字孪生跌落机监控系统 (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

主要功能:

  1. 设备管理: 管理多台跌落机及其工位状态
  2. 数据轮询: 每个设备独立线程轮询PLC数据 (0.6秒间隔)
  3. 状态同步: PLC状态与工位状态双向同步
  4. 测试结果处理: 解析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. 性能优化点

  1. 批量数据库写入 - 2秒间隔批量提交
  2. 连接状态缓存 - 避免频繁读取PLC
  3. HTTP连接池 - requests.Session复用TCP连接
  4. WebSocket消息合并 - 相同设备的多次更新合并为一次
  5. 线程池上传 - 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 维护者: 系统架构团队