编辑 | blame | 历史 | 原始文档

MES PLC Send 项目文档

📋 项目概述

MES PLC Send 是一个基于 Spring Boot 的多设备联合测试系统,支持 PLC 设备管理、设备组配置、多设备任务编排和执行。系统实现了"模板 + 实例"的设计模式,支持一个设备类型模板对应多个设备实例,实现了设备间的协调和数据传递。

技术栈
- Spring Boot 2.x
- MyBatis-Plus
- S7NetPlus(PLC通信)
- MySQL
- Server-Sent Events (SSE) 实时推送

服务端口:10018

🎯 核心功能

1. 设备管理

  • 设备配置管理(PLC IP、设备类型、模块名称等)
  • 设备状态监控(在线/离线/忙碌/错误/维护中)
  • 支持5种设备类型:
  • 大车设备 (LOAD_VEHICLE):支持多实例协调,自动状态管理,MES任务处理
  • 大理片笼 (LARGE_GLASS):格子范围配置,逻辑判断
  • 卧转立扫码 (WORKSTATION_SCANNER):定时扫描,MES数据读取
  • 卧转立 (WORKSTATION_TRANSFER):30s缓冲判定,批量处理
  • 卧式缓存 (GLASS_STORAGE):玻璃存储管理(已实现,但当前不使用)

2. 设备组管理

  • 设备组配置(支持最大并发设备数控制)
  • 设备组拓扑可视化
  • 设备依赖关系管理(优先级、角色、连接顺序)
  • 设备组任务编排

3. 多设备任务执行

  • 串行执行:按设备连接顺序执行
  • 并行执行:支持多设备并行执行,通过 max_concurrent_devices 控制并发数
  • 实时监控:基于 SSE(Server-Sent Events)的实时状态推送
  • 错误处理:自动重试机制,支持指数退避
  • 数据传递:设备间数据共享和状态同步

4. 设备协调服务

  • 设备间数据传递机制
  • 设备状态同步
  • 设备依赖管理
  • 多实例协调(如多车协调)

🏗️ 架构设计

目录结构

mes-plcSend/
├── device/                          # 设备管理层
│   ├── entity/                      # 实体类
│   │   ├── DeviceConfig.java        # 设备配置
│   │   ├── DeviceGroupConfig.java   # 设备组配置
│   │   ├── DeviceGroupRelation.java # 设备组关系
│   │   ├── DeviceStatus.java        # 设备状态
│   │   └── GlassInfo.java           # 玻璃信息
│   ├── service/                     # 服务层
│   │   ├── DeviceConfigService.java
│   │   ├── DeviceGroupConfigService.java
│   │   ├── DeviceCoordinationService.java  # 设备协调服务
│   │   └── GlassInfoService.java
│   └── controller/                  # 控制器
│       ├── DeviceConfigController.java
│       ├── DeviceGroupController.java
│       └── DevicePlcController.java
│
├── interaction/                     # 交互逻辑层
│   ├── base/                        # 基础接口
│   │   ├── BaseDeviceLogicHandler.java
│   │   ├── DeviceInteraction.java
│   │   └── InteractionContext.java
│   │
│   ├── vehicle/                     # 大车设备专用包
│   │   ├── handler/
│   │   │   └── LoadVehicleLogicHandler.java  # 共享逻辑处理器
│   │   ├── flow/
│   │   │   └── LoadVehicleInteraction.java
│   │   ├── coordination/
│   │   │   ├── VehicleStatusManager.java      # 状态管理器
│   │   │   └── VehicleCoordinationService.java # 协调服务
│   │   └── model/
│   │       ├── VehicleStatus.java
│   │       ├── VehiclePosition.java
│   │       ├── VehicleState.java
│   │       ├── VehiclePath.java
│   │       └── VehicleTask.java
│   │
│   ├── workstation/                 # 卧转立设备包
│   │   ├── base/
│   │   │   └── WorkstationBaseHandler.java
│   │   ├── config/
│   │   │   └── WorkstationLogicConfig.java
│   │   ├── scanner/
│   │   │   └── handler/
│   │   │       └── HorizontalScannerLogicHandler.java
│   │   └── transfer/
│   │       └── handler/
│   │           └── HorizontalTransferLogicHandler.java
│   │
│   ├── largeglass/                  # 大理片笼设备包
│   │   ├── handler/
│   │   │   └── LargeGlassLogicHandler.java
│   │   ├── config/
│   │   │   └── LargeGlassConfig.java
│   │   └── model/
│   │       └── GridRange.java
│   │
│   ├── flow/                        # 交互流程(通用)
│   │   ├── GlassStorageInteraction.java
│   │   └── LargeGlassInteraction.java
│   │
│   └── impl/                        # 简单设备实现
│       └── GlassStorageLogicHandler.java
│
├── task/                            # 任务管理层
│   ├── entity/
│   │   ├── MultiDeviceTask.java     # 多设备任务
│   │   └── TaskStepDetail.java      # 任务步骤详情
│   ├── service/
│   │   ├── MultiDeviceTaskService.java
│   │   ├── TaskExecutionEngine.java  # 任务执行引擎
│   │   └── TaskStatusNotificationService.java  # SSE推送服务
│   ├── controller/
│   │   ├── MultiDeviceTaskController.java
│   │   └── TaskStatusNotificationController.java
│   └── model/
│       ├── RetryPolicy.java         # 重试策略
│       ├── TaskExecutionContext.java
│       └── TaskExecutionResult.java
│
├── service/                         # PLC服务层
│   ├── PlcDynamicDataService.java   # PLC动态数据服务
│   └── PlcTestWriteService.java
│
└── s7/                              # S7通信
    └── provider/
        └── S7SerializerProvider.java

核心设计模式

1. 模板 + 实例模式

设计理念:一个设备类型模板(共享逻辑)+ 多个设备实例(独立状态)

LoadVehicleLogicHandler (共享逻辑处理器)
    ↓
┌─────────┐  ┌─────────┐  ┌─────────┐
│ 大车实例1│  │ 大车实例2│  │ 大车实例3│
│ 状态独立 │  │ 状态独立 │  │ 状态独立 │
└─────────┘  └─────────┘  └─────────┘

优势
- 共享逻辑:所有实例使用同一个逻辑处理器
- 独立状态:每个实例有独立的运行时状态
- 灵活配置:设备组可以包含任意数量的实例
- 易于扩展:新增实例只需在数据库添加记录

2. 混合分层架构

  • 复杂设备(如大车):独立包,包含协调服务、状态管理等
  • 简单设备(如玻璃存储):放在通用包中

🔧 核心组件说明

1. 大车设备(LoadVehicle)

功能特性

  • 空闲状态监控:没有任务时,plcRequest 保持为 1
  • MES任务读取:当 mesSend=1 时,读取 MES 参数(玻璃ID、起始位置、目标位置等)
  • 位置映射:将 MES 位置编号(如 900、901)映射为实际网格位置(如 100、500)
  • 时间计算:根据车辆速度(grids/second)、当前位置、目标位置计算 gotimecartime
  • 状态管理state1~6 状态流转(0→1→2),自动触发 MES 汇报
  • 自动协调:当 state=1(上车完成)时,自动将"卧转立"设备的 plcRequest 设置为 0
  • 出片逻辑:支持进片和出片任务,根据 startSlotoutboundSlotRanges 自动判断任务类型

配置参数(extraParams.deviceLogic)

{
  "vehicleCapacity": 6,
  "vehicleSpeed": 1.0,
  "minRange": 1,
  "maxRange": 100,
  "homePosition": 50,
  "idleMonitorIntervalMs": 1000,
  "taskMonitorIntervalMs": 1000,
  "mesConfirmTimeoutMs": 30000,
  "positionMapping": {
    "900": 100,
    "901": 500
  },
  "outboundSlotRanges": [
    {"start": 1000, "end": 2000}
  ],
  "gridPositionMapping": {
    "1000": 80
  }
}

状态流转

IDLE (空闲) → EXECUTING (执行中) → IDLE (空闲)

2. 卧转立扫码(HorizontalScanner)

功能特性

  • 定时扫描:可配置扫描间隔(默认 10s)
  • MES数据读取:当 mesSend=1 时,读取玻璃信息(mesGlassIdmesWidthmesHeightworkLine
  • 数据落库:将玻璃信息保存到 glass_info

配置参数(extraParams.deviceLogic)

{
  "scanIntervalMs": 10000,
  "workLine": 1
}

3. 卧转立(HorizontalTransfer)

功能特性

  • 30s缓冲判定:等待 30s,如果没有下一片玻璃扫码,则认为是最后一片
  • 容量判断:判断能否放下第二片玻璃
  • 批量处理:将多片玻璃组装成批次
  • PLC写入:写入 plcGlassId1~6plcGlassCountinPositionplcRequest

配置参数(extraParams.deviceLogic)

{
  "scanIntervalMs": 10000,
  "bufferTimeoutMs": 30000,
  "vehicleCapacity": 2,
  "monitorIntervalMs": 1000,
  "workLine": "LINE_001",
  "positionValue": 100
}

4. 大理片笼(LargeGlass)

功能特性

  • 格子范围配置:支持多行格子配置(如第一行 1~52 格,第二行 53~101 格)
  • 格子尺寸配置:每格的长、宽、厚可配置
  • 逻辑判断:用于位置验证和格子管理,不涉及 PLC 写入

配置参数(extraParams.deviceLogic)

{
  "gridRanges": [
    {"row": 1, "start": 1, "end": 52},
    {"row": 2, "start": 53, "end": 101}
  ],
  "gridLength": 2000,
  "gridWidth": 1500,
  "gridThickness": 5
}

🚀 使用指南

1. 设备配置

创建设备

DeviceConfig device = new DeviceConfig();
device.setDeviceId("DEVICE_001");
device.setDeviceCode("DEV_001");
device.setDeviceName("大车设备1");
device.setDeviceType(DeviceConfig.DeviceType.LOAD_VEHICLE);
device.setPlcIp("192.168.1.101");
device.setPlcPort(102);
device.setPlcType(DeviceConfig.PlcType.S7_1200);
device.setModuleName("DB1");
device.setProjectId(1L);
device.setEnabled(true);
// 设置逻辑参数
device.setExtraParams(extraParams);
deviceConfigService.createDevice(device);

配置设备逻辑参数

extraParams.deviceLogic 中配置设备特定的逻辑参数,如车辆速度、位置映射等。

2. 设备组配置

创建设备组

DeviceGroupConfig group = new DeviceGroupConfig();
group.setGroupCode("GROUP_001");
group.setGroupName("生产线A");
group.setGroupType(1);  // 1-生产线,2-测试线,3-辅助设备组
group.setProjectId(1L);
group.setStatus(1);  // 0-停用,1-启用,3-维护中
group.setMaxConcurrentDevices(3);  // 最大并发设备数
group.setHeartbeatInterval(30);
group.setCommunicationTimeout(5000);
deviceGroupService.createGroup(group);

添加设备到设备组

// 添加设备,设置优先级、角色、连接顺序
deviceGroupService.addDevicesToGroup(groupId, deviceIds, priorities, roles, connectionOrders);

3. 任务执行

启动多设备任务

MultiDeviceTaskRequest request = new MultiDeviceTaskRequest();
request.setGroupId(groupId);
request.setTaskName("测试任务");
request.setProjectId("PROJECT_001");
request.setParameters(taskParameters);
MultiDeviceTask task = multiDeviceTaskService.startTask(request);

API 端点

  • POST /device/task/start - 启动任务
  • POST /device/task/list - 查询任务列表
  • GET /device/task/{taskId} - 查询任务详情
  • GET /device/task/{taskId}/steps - 查询任务步骤详情
  • POST /device/task/{taskId}/cancel - 取消任务

实时监控(SSE)

前端通过 SSE 连接实时接收任务状态更新:
```javascript
// 监听指定任务
const eventSource = new EventSource('/task/notification/sse?taskId=xxx');
eventSource.addEventListener('taskStatus', (event) => {
const data = JSON.parse(event.data);
// 处理任务状态更新
});
eventSource.addEventListener('stepUpdate', (event) => {
const data = JSON.parse(event.data);
// 处理步骤更新
});

// 监听所有任务
const eventSourceAll = new EventSource('/task/notification/sse/all');
```

SSE 端点
- GET /task/notification/sse?taskId=xxx - 监听指定任务
- GET /task/notification/sse/all - 监听所有任务
- POST /task/notification/close/{taskId} - 关闭指定任务的SSE连接
- POST /task/notification/close/all - 关闭所有SSE连接

📊 数据库设计

核心表结构

device_config(设备配置表)

  • id:主键(BIGINT)
  • device_id:设备唯一标识(VARCHAR(50),唯一)
  • device_code:设备编码(VARCHAR(50),唯一)
  • device_name:设备名称(VARCHAR(100))
  • device_type:设备类型(VARCHAR(50))
  • project_id:所属项目ID(BIGINT)
  • plc_ip:PLC IP地址(VARCHAR(15))
  • plc_port:PLC端口(INT)
  • plc_type:PLC类型(VARCHAR(20))
  • module_name:模块名称(VARCHAR(50))
  • status:设备状态(VARCHAR(20))
  • is_primary:是否主控设备(BOOLEAN)
  • enabled:是否启用(BOOLEAN)
  • config_json:设备特定配置(TEXT,JSON格式)
  • extra_params:扩展参数(JSON)
  • description:设备描述(VARCHAR(200))
  • is_deleted:是否删除(INT,0-否,1-是)
  • created_timeupdated_time:创建/更新时间
  • created_byupdated_by:创建/更新人

device_group_config(设备组配置表)

  • id:主键(BIGINT)
  • group_code:设备组编码(VARCHAR(50),唯一)
  • group_name:设备组名称(VARCHAR(100))
  • group_type:设备组类型(INT,1-生产线,2-测试线,3-辅助设备组)
  • project_id:所属项目ID(BIGINT)
  • status:设备组状态(INT,0-停用,1-启用,3-维护中)
  • max_concurrent_devices:最大并发设备数(INT)
  • heartbeat_interval:心跳检测间隔(INT,秒)
  • communication_timeout:通信超时时间(INT,毫秒)
  • description:设备组描述(VARCHAR(200))
  • extra_config:扩展配置(JSON)
  • is_deleted:是否删除(INT)
  • created_timeupdated_time:创建/更新时间
  • created_byupdated_by:创建/更新人

device_group_relation(设备组关系表)

  • id:主键(BIGINT)
  • group_id:设备组ID(BIGINT)
  • device_id:设备ID(BIGINT)
  • priority:设备在组内的优先级(INT,1-最高,10-最低)
  • role:设备在组内的角色(INT,1-主控,2-协作,3-监控)
  • status:设备在该组中的状态(INT,0-未配置,1-正常,2-故障,3-维护)
  • connection_order:连接顺序(INT,数值越小越先连接)
  • relation_desc:关联描述(VARCHAR(200))
  • extra_params:扩展参数(JSON)
  • is_deleted:是否删除(INT)
  • created_timeupdated_time:创建/更新时间
  • created_byupdated_by:创建/更新人

multi_device_task(多设备任务表)

  • id:主键(BIGINT)
  • task_id:任务唯一标识(VARCHAR(50),唯一)
  • group_id:设备组ID(VARCHAR(50))
  • project_id:项目ID(VARCHAR(50))
  • status:任务状态(ENUM:PENDING, RUNNING, COMPLETED, FAILED, CANCELLED)
  • current_step:当前执行步骤(INT)
  • total_steps:总步骤数(INT)
  • start_time:开始时间(DATETIME)
  • end_time:结束时间(DATETIME)
  • error_message:错误信息(TEXT)
  • result_data:结果数据(JSON)
  • created_timeupdated_time:创建/更新时间

task_step_detail(任务步骤详情表)

  • id:主键(BIGINT)
  • task_id:任务ID(VARCHAR(50))
  • step_order:步骤顺序(INT)
  • device_id:设备ID(VARCHAR(50))
  • step_name:步骤名称(VARCHAR(100))
  • status:步骤状态(ENUM:PENDING, RUNNING, COMPLETED, FAILED, SKIPPED)
  • start_time:步骤开始时间(DATETIME)
  • end_time:步骤结束时间(DATETIME)
  • duration_ms:执行耗时(BIGINT,毫秒)
  • input_data:输入数据(JSON)
  • output_data:输出数据(JSON)
  • error_message:错误信息(TEXT)
  • retry_count:重试次数(INT)
  • created_time:创建时间(DATETIME)

glass_info(玻璃信息表)

  • id:主键(BIGINT)
  • glass_id:玻璃ID(VARCHAR(50))
  • width:宽度(DECIMAL)
  • height:高度(DECIMAL)
  • work_line:产线编号(VARCHAR(50))
  • scan_time:扫码时间(DATETIME)
  • status:状态(VARCHAR(20))
  • created_timeupdated_time:创建/更新时间

device_status(设备状态监控表)

  • id:主键(BIGINT)
  • device_id:设备ID(VARCHAR(50))
  • task_id:关联任务ID(VARCHAR(50),可选)
  • status:设备状态(ENUM:ONLINE, OFFLINE, BUSY, ERROR, MAINTENANCE)
  • last_heartbeat:最后心跳时间(DATETIME)
  • cpu_usage:CPU使用率(DECIMAL(5,2))
  • memory_usage:内存使用率(DECIMAL(5,2))
  • plc_connection_status:PLC连接状态(ENUM:CONNECTED, DISCONNECTED, ERROR)
  • current_operation:当前操作(VARCHAR(100))
  • operation_progress:操作进度(DECIMAL(5,2),0-100)
  • alert_message:告警信息(TEXT)
  • created_time:记录时间(DATETIME)

🔄 任务执行流程

串行执行流程

1. 创建任务记录(status = PENDING)
2. 获取设备组中的设备列表(按 connection_order 排序)
3. 依次执行每个设备:
   a. 检查前置条件
   b. 更新步骤状态为 RUNNING
   c. 执行设备交互逻辑
   d. 传递数据到下一个设备
   e. 更新步骤状态为 COMPLETED
4. 所有设备执行完成后,更新任务状态为 COMPLETED

并行执行流程

1. 创建任务记录(status = PENDING)
2. 获取设备组中的设备列表
3. 使用线程池并行执行设备:
   a. 使用 max_concurrent_devices 控制并发数
   b. 每个设备独立执行
   c. 等待所有设备完成
4. 所有设备执行完成后,更新任务状态为 COMPLETED

⚙️ 配置说明

应用配置(application.yml)

server:
  port: 10018

spring:
  profiles:
    active: dev
  application:
    name: plcSend
  liquibase:
    enabled: true
    change-log: classpath:changelog/changelogBase.xml

# PLC配置
s7:
  load:
    dbArea: DB1
    beginIndex: 0
  raw:
    dbArea: DB2
    beginIndex: 0

# PLC模拟配置
plc:
  simulate:
    enabled: false
    interval: 5000
    failure-rate: 0
    task-count: 10
    task-type: normal

# MES配置
mes:
  width: 2800
  height: 5000

设备逻辑参数配置

每个设备类型的逻辑参数在 extraParams.deviceLogic 中配置,具体参数见各设备类型的说明。

🛠️ 扩展开发

添加新设备类型

  1. 在 DeviceConfig.DeviceType 中添加常量
    java public static final class DeviceType { public static final String NEW_DEVICE = "新设备类型"; }

  2. 创建设备处理器
    ```java
    @Component
    public class NewDeviceLogicHandler extends BaseDeviceLogicHandler {
    @Override
    public String getDeviceType() {
    return DeviceConfig.DeviceType.NEW_DEVICE;
    }

    @Override
    protected DevicePlcVO.OperationResult doExecute(
    DeviceConfig deviceConfig,
    String operation,
    Map<String, Object> params,
    Map<String, Object> logicParams) {
    // 实现设备逻辑
    }
    }
    ```

  3. 创建交互流程(可选)
    java @Component public class NewDeviceInteraction implements DeviceInteraction { @Override public InteractionResult execute(InteractionContext context) { // 实现交互流程 } }

添加设备协调逻辑

对于需要多实例协调的复杂设备,可以创建专用包:
interaction/ └── newdevice/ ├── handler/ ├── coordination/ └── model/

📝 注意事项

1. 状态管理

  • 大车设备的状态存储在内存中(VehicleStatusManager),服务重启后会丢失
  • 如需持久化,可以扩展支持数据库存储

2. 并发安全

  • 状态管理器使用 ConcurrentHashMap,支持并发访问
  • 任务执行使用线程池和 max_concurrent_devices 控制并发

3. 错误处理

  • 支持自动重试机制(默认最多3次)
  • 支持指数退避策略
  • 错误分类:网络错误、超时错误可重试,业务错误不可重试

4. 实时监控

  • SSE 连接超时时间:30分钟
  • 支持监听指定任务或所有任务
  • 连接断开后需要重新连接

5. 设备类型说明

  • 卧式缓存GLASS_STORAGE):代码中已实现,但当前业务场景不使用,保留用于未来扩展

🎯 已完成功能

✅ 核心功能

  • [x] 设备管理(配置、状态监控)
  • [x] 设备组管理(并发控制、优先级、角色)
  • [x] 多设备任务执行引擎(串行/并行)
  • [x] 设备协调服务(数据传递、状态同步)
  • [x] 实时监控推送(SSE)
  • [x] 错误处理和重试机制

✅ 设备类型支持

  • [x] 大车设备(多实例协调、状态管理、MES任务处理、进片/出片)
  • [x] 卧转立扫码(定时扫描、数据落库)
  • [x] 卧转立(30s缓冲、批量处理)
  • [x] 大理片笼(格子配置、逻辑判断)
  • [x] 卧式缓存(已实现,当前不使用)

✅ API 端点

  • [x] 设备管理 API(/device/*
  • [x] 设备组管理 API(/device/group/*
  • [x] 多设备任务 API(/device/task/*
  • [x] SSE 实时通知 API(/task/notification/*

📚 相关文档

  • 数据库迁移脚本:src/main/resources/db/migration/
  • API 文档:通过 Swagger 访问 /swagger-ui.html
  • 前端文档:见 mes-web 项目

📞 联系方式

如有问题或建议,请联系开发团队。