opt:md文件优化

This commit is contained in:
2026-04-16 14:01:59 +08:00
parent b87d2d6cea
commit 993f6ffe45
12 changed files with 2473 additions and 44 deletions

View File

@@ -2,7 +2,7 @@ package org.nl.acs.autoThread.autoRun;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.nl.acs.autoThread.autoRunThread.service.enums.ThreadUsedStatusEnum;
import org.nl.acs.autoThread.service.enums.ThreadUsedStatusEnum;
import org.nl.acs.autoThread.service.enums.ThreadStatusEnum;
import java.util.Date;

View File

@@ -1,7 +1,7 @@
package org.nl.acs.autoThread.service.dto;
import lombok.Data;
import org.nl.acs.autoThread.autoRunThread.service.enums.ThreadUsedStatusEnum;
import org.nl.acs.autoThread.service.enums.ThreadUsedStatusEnum;
import org.nl.acs.autoThread.service.enums.ThreadStatusEnum;
import java.util.Date;

View File

@@ -1,4 +1,4 @@
package org.nl.acs.autoThread.autoRunThread.service.enums;
package org.nl.acs.autoThread.service.enums;
/**
* Demo class
*

View File

@@ -7,7 +7,7 @@ import org.apache.commons.lang3.StringUtils;
import org.nl.ApplicationAutoInitial;
import org.nl.acs.autoThread.autoRun.AbstractAutoRunnable;
import org.nl.acs.autoThread.StartConfig;
import org.nl.acs.autoThread.autoRunThread.service.enums.ThreadUsedStatusEnum;
import org.nl.acs.autoThread.service.enums.ThreadUsedStatusEnum;
import org.nl.acs.autoThread.service.AutoRunService;
import org.nl.acs.autoThread.service.dto.AutoRunDto;
import org.nl.common.exception.BadRequestException;

View File

@@ -1,15 +1,415 @@
### appInit
项目启动时候对项目进行初始化初始化器包括6项目
1.AutoRunService :根据配置判断哪些自动线程需要启动
2.自动线程继承AbstractAutoRunnable目前系统一共有4个自动线程
#### DeviceExecuteAutoRun
设备自动线程,完成每个设备驱动的加载
设备驱动通过while循环判断信号状态
#### NDCAutoRun
NDC自动线程通过该线程与NDC创建socket通讯做phace逻辑交互
该版本废弃统一改agvKit交互
#### DeviceOpcSynchronizeAutoRun
同步所有OpcService信号
该版本固定使用KEP-SERVER 进行信号同步
#### SocketListenerAutoRun
socket同步监听测试类在startconfig中配置不用
# 初始化自动线程文档
## 1. 自动线程系统概述
自动线程系统是 NL Admin System 的核心组件,负责管理系统启动时自动运行的后台任务。采用 **模板方法模式** + **服务发现机制**,实现线程的自动注册、启动和管理。
### 1.1 整体架构
```
┌─────────────────────────────────────────────────────────────┐
│ 应用启动阶段 │
├─────────────────────────────────────────────────────────────┤
│ ApplicationAutoInitial (Spring自动初始化接口) │
│ │ │
│ ▼ │
│ AutoRunServiceImpl.autoInitial() │
│ │ │
│ ├──► 扫描所有 AbstractAutoRunnable Bean │
│ ├──► 过滤检查 (code空/重复/禁止列表) │
│ ├──► 注册到 code_autoRun_Map │
│ └──► 创建线程并启动 │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ AbstractAutoRunnable.run() │ │
│ │ ┌─────────────────────────────────────────────┐ │ │
│ │ │ before() → autoRun() → after() │ │ │
│ │ │ │ │ │ │ │ │
│ │ │ 前置处理 核心循环 后置清理 │ │ │
│ │ └─────────────────────────────────────────────┘ │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
### 1.2 核心组件
| 组件 | 职责 | 说明 |
|------|------|------|
| `ApplicationAutoInitial` | 自动初始化接口 | Spring启动时自动调用 `autoInitial()` |
| `AutoRunServiceImpl` | 线程管理器 | 负责线程的注册、启动、停止、状态查询 |
| `AbstractAutoRunnable` | 抽象基类 | 定义线程生命周期模板 |
| `StartConfig` | 启动配置 | 控制全局开关和禁止列表 |
---
## 2. 初始化流程
### 2.1 启动流程详解
```java
// AutoRunServiceImpl.autoInitial()
@Override
public void autoInitial() throws Exception {
// 1. 全局开关检查
if (StartConfig.Need_Stop) {
System.out.println("---未开启自动线程AutoRun初始化终止---");
return; // 全局禁止启动
}
// 2. 遍历所有AbstractAutoRunnable Bean
for (AbstractAutoRunnable autoRunnable : abstractAutoRunnableList) {
// 检查1: code不能为空
if (StringUtils.isEmpty(autoRunnable.getCode())) {
autoRunnable.setMessage("code为空未加载");
continue;
}
// 检查2: code不能重复
if (partialJudg.contains(autoRunnable.getCode())) {
autoRunnable.setMessage("code重复而未加载");
continue;
}
// 检查3: 不在禁止列表中
if (StartConfig.AutoRun_Ban.contains(autoRunnable.getCode())) {
autoRunnable.setMessage("配置了禁止启动");
continue;
}
// 通过所有检查注册到Map
autoRunnable.setUsedStatus(ThreadUsedStatusEnum.used);
code_autoRun_Map.put(autoRunnable.getCode(), autoRunnable);
}
// 3. 启动所有已注册的线程
for (AbstractAutoRunnable autoRunnable : code_autoRun_Map.values()) {
final Thread thread = new Thread(autoRunnable);
code_Thread_Map.put(autoRunnable.getCode(), thread);
thread.start();
}
}
```
### 2.2 过滤规则
| 检查项 | 条件 | 处理方式 |
|--------|------|----------|
| **code为空** | `StringUtils.isEmpty(code)` | 跳过,记录消息 |
| **code重复** | `partialJudg.contains(code)` | 跳过,记录消息 |
| **在禁止列表** | `StartConfig.AutoRun_Ban.contains(code)` | 跳过,记录消息 |
---
## 3. AbstractAutoRunnable 抽象基类
### 3.1 类结构
```java
@Data
public abstract class AbstractAutoRunnable implements Runnable {
private ThreadStatusEnum status; // 线程执行状态 (run/stop)
private Date startTime; // 线程开始时间
private Date stopTime; // 线程结束时间
private String message; // 状态消息
private ThreadUsedStatusEnum usedStatus; // 初始化状态 (used/unUsed)
@Override
public void run() {
this.setStatus(ThreadStatusEnum.run);
this.setStartTime(new Date());
try {
this.setMessage("自动线程初始化中");
this.before(); // 前置处理
this.autoRun(); // 核心业务(子类实现死循环)
this.setMessage("自动线程正常启动");
} catch (Throwable throwable) {
log.error(this.getCode() + "自动线程执行异常", throwable);
} finally {
this.setStopTime(new Date());
this.setMessage("自动线程关闭");
this.setStatus(ThreadStatusEnum.stop);
this.after(); // 后置清理
}
}
// 子类必须实现的方法
public abstract String getCode(); // 返回线程唯一标识
public abstract String getName(); // 返回线程名称
public abstract void autoRun() throws Exception; // 核心业务逻辑
// 可选覆盖方法
public void before() throws Exception {} // 前置处理
public void after() {} // 后置清理
public void stop() { this.after(); } // 停止线程
}
```
### 3.2 生命周期方法
| 方法 | 调用时机 | 用途 | 是否必须实现 |
|------|----------|------|--------------|
| `before()` | `run()` 开始时 | 初始化资源、建立连接 | 否 |
| `autoRun()` | `before()` 之后 | 核心业务逻辑(死循环) | **是** |
| `after()` | `run()` 结束时 | 释放资源、清理 | 否 |
| `stop()` | 手动停止时 | 触发停止流程 | 否 |
---
## 4. 启动配置
### 4.1 StartConfig 配置类
```java
public class StartConfig {
/**
* 全局开关true=禁止所有自动线程启动false=允许启动
*/
public static Boolean Need_Stop = Boolean.TRUE;
/**
* 禁止启动的线程code列表
*/
public static List<String> AutoRun_Ban = new ArrayList<>();
static {
AutoRun_Ban.add("TaskFeedbackAutoRun"); // 任务反馈线程(测试用)
AutoRun_Ban.add("SocketListenerAutoRun"); // Socket监听线程测试用
}
}
```
### 4.2 配置说明
| 配置项 | 类型 | 默认值 | 说明 |
|--------|------|--------|------|
| `Need_Stop` | Boolean | `true` | 全局开关,设为`false`时启动所有自动线程 |
| `AutoRun_Ban` | List | 包含2个元素 | 精确匹配线程code匹配到的线程不会启动 |
---
## 5. 现有自动线程
### 5.1 线程列表
| 线程类 | Code | 职责 | 状态 |
|--------|------|------|------|
| `DeviceExecuteAutoRunable` | `DeviceExecuteAutoRun` | 设备执行线程,执行所有设备驱动 | **运行中** |
| `OpcSignalSyncAutoRunnable` | `OpcSignalSyncAutoRun` | OPC信号同步线程 | **运行中** |
| `SocketListenerAutoRun` | `SocketListenerAutoRun` | Socket监听测试线程 | **已禁止** |
### 5.2 DeviceExecuteAutoRunable
**职责**:定期执行所有设备驱动的自动任务
```java
public class DeviceExecuteAutoRunable extends AbstractAutoRunnable {
@Override
public void autoRun() throws Exception {
// 等待OPC同步完成最多60秒
for (int i = 0; !OpcConfig.OPC_START_TAG; ++i) {
Thread.sleep(1000L);
if (i > 60) break;
}
// 死循环执行设备任务
while (true) {
Thread.sleep(100);
List<BaseDeviceDriver> deviceDrivers = deviceAppService.findDeviceDriver(BaseDeviceDriver.class);
for (BaseDeviceDriver driver : deviceDrivers) {
executorService.submit(() -> driver.executeAuto());
}
}
}
}
```
### 5.3 OpcSignalSyncAutoRunnable
**职责**同步所有OPC服务器的信号到系统内存
```java
public class OpcSignalSyncAutoRunnable extends AbstractAutoRunnable {
@Override
public void autoRun() throws Exception {
// 同步OPC信号逻辑
while (true) {
// 读取OPC服务器数据
// 更新到统一数据访问器(UDW)
Thread.sleep(50); // 控制同步频率
}
}
}
```
---
## 6. 线程管理 API
### 6.1 AutoRunService 接口
```java
public interface AutoRunService {
/**
* 启动指定线程
* @param code 线程唯一标识
*/
void startThread(String code);
/**
* 停止指定线程
* @param code 线程唯一标识
*/
void stopThread(String code);
/**
* 获取所有线程状态
* @return 线程状态列表
*/
List<AutoRunDto> findAll();
}
```
### 6.2 使用示例
```java
// 注入线程服务
@Autowired
private AutoRunService autoRunService;
// 启动线程
autoRunService.startThread("DeviceExecuteAutoRun");
// 停止线程
autoRunService.stopThread("DeviceExecuteAutoRun");
// 查询所有线程状态
List<AutoRunDto> threads = autoRunService.findAll();
for (AutoRunDto dto : threads) {
System.out.println("线程: " + dto.getName() + ", 状态: " + dto.getStatus());
}
```
### 6.3 AutoRunDto 数据结构
| 字段 | 类型 | 说明 |
|------|------|------|
| `code` | String | 线程唯一标识 |
| `name` | String | 线程名称 |
| `status` | String | 执行状态 (run/stop) |
| `startTime` | Date | 启动时间 |
| `stopTime` | Date | 停止时间 |
| `message` | String | 状态消息 |
| `thread_alive` | Boolean | 线程是否存活 |
| `thread_name` | String | JVM线程名称 |
| `thread_id` | String | JVM线程ID |
| `thread_state` | String | JVM线程状态 |
---
## 7. 扩展开发指南
### 7.1 创建新的自动线程
1. **继承 AbstractAutoRunnable**
```java
@Component
public class MyCustomAutoRun extends AbstractAutoRunnable {
@Override
public String getCode() {
return "MyCustomAutoRun"; // 唯一标识
}
@Override
public String getName() {
return "我的自定义线程";
}
@Override
public void autoRun() throws Exception {
// 死循环执行业务逻辑
while (true) {
// 执行任务...
Thread.sleep(1000); // 控制执行频率
}
}
@Override
public void before() throws Exception {
// 初始化资源(可选)
super.before();
}
@Override
public void after() {
// 清理资源(可选)
super.after();
}
}
```
2. **配置控制**
```java
// 在 StartConfig 中配置(如需禁止启动)
static {
AutoRun_Ban.add("TaskFeedbackAutoRun");
AutoRun_Ban.add("SocketListenerAutoRun");
// AutoRun_Ban.add("MyCustomAutoRun"); // 如需禁止,取消注释
}
```
### 7.2 注意事项
- **线程安全**`autoRun()` 中的共享资源需要加锁保护
- **异常处理**:建议在 `autoRun()` 内部捕获异常,避免线程意外终止
- **退出机制**:建议使用 `volatile` 标志位控制循环退出
---
## 8. 关键设计要点
### 8.1 服务发现机制
Spring 自动扫描所有 `AbstractAutoRunnable` 子类:
```java
@Autowired(required = false)
private List<AbstractAutoRunnable> abstractAutoRunnableList;
```
### 8.2 线程安全
```java
@Override
public synchronized void startThread(String code) { ... }
@Override
public synchronized void stopThread(String threadCode) { ... }
```
### 8.3 状态管理
使用枚举类管理状态:
```java
// ThreadStatusEnum - 执行状态
public enum ThreadStatusEnum {
run, // 运行中
stop // 已停止
}
// ThreadUsedStatusEnum - 初始化状态
public enum ThreadUsedStatusEnum {
used, // 已使用(已注册)
unUsed // 未使用(未注册)
}
```
---
**文档版本**: v1.0
**创建日期**: 2026年4月
**适用模块**: NL Admin System - 自动线程模块

View File

@@ -1,15 +1,509 @@
### appInit
项目启动时候对项目进行初始化初始化器包括6项目
1.AutoRunService :根据配置判断哪些自动线程需要启动
2.自动线程继承AbstractAutoRunnable目前系统一共有4个自动线程
#### DeviceExecuteAutoRun
设备自动线程,完成每个设备驱动的加载
设备驱动通过while循环判断信号状态
#### NDCAutoRun
NDC自动线程通过该线程与NDC创建socket通讯做phace逻辑交互
该版本废弃统一改agvKit交互
#### DeviceOpcSynchronizeAutoRun
同步所有OpcService信号
该版本固定使用KEP-SERVER 进行信号同步
#### SocketListenerAutoRun
socket同步监听测试类在startconfig中配置不用
# 设备信息管理文档
## 1. 设备信息加载流程
### 1.1 整体架构
设备信息加载采用 **服务发现 + 驱动绑定** 的设计模式:
```
┌─────────────────────────────────────────────────────────────────┐
│ 应用启动阶段 │
├─────────────────────────────────────────────────────────────────┤
│ ApplicationAutoInitial (自动初始化接口) │
│ │ │
│ ▼ │
│ DeviceAppServiceImpl.autoInitial() │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ reload() 方法 │ │
│ │ 1. 从数据库查询设备基本信息 (DeviceMapper) │ │
│ │ 2. 关联设备扩展信息 (DeviceExtraMapper) │ │
│ │ 3. 关联OPC/PLC配置 (OpcMapper/OpcPlcMapper) │ │
│ │ 4. 绑定设备驱动 (DeviceDriverDefination) │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ 内存缓存 (devices List) │ │
│ │ - devices: 设备对象列表 │ │
│ │ - code_indexs: 设备编码索引 │ │
│ └─────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### 1.2 核心代码流程
#### ① 自动初始化触发
```java
// DeviceAppServiceImpl.java
@Override
public void autoInitial() throws Exception {
log.info("加载所有设备");
this.reload(); // 核心加载方法
}
```
#### ② 设备信息查询与组装
```java
@Override
public synchronized void reload() {
// 1. 查询所有设备(包含扩展信息)
List<DeviceManageDto> deviceManageDtos = this.queryAllWithExtra();
// 2. 初始化内存缓存
this.devices = new ArrayList();
this.code_indexs = new ArrayList();
// 3. 遍历设备创建Device对象并绑定驱动
for (DeviceManageDto deviceManage : deviceManageDtos) {
Device device = new Device();
BeanUtil.copyProperties(deviceManage, device);
this.devices.add(device);
this.code_indexs.add(device.getDevice_code());
// 4. 绑定设备驱动(关键步骤)
if (!StrUtil.isEmpty(device.getDriver_code())) {
DeviceDriverDefination defination =
deviceDriverDefinationAppService.getDeviceDriverDefination(deviceManage.getDriver_code());
if (defination != null) {
device.setDeviceDriverDefination(defination);
DeviceDriver driver = defination.getDriverInstance(device); // 创建驱动实例
device.setDeviceDriver(driver);
}
}
}
}
```
#### ③ 数据库查询逻辑
`queryAllWithExtra()` 方法执行以下查询:
| 数据表 | 查询内容 | 用途 |
|--------|----------|------|
| `acs_device` | 设备基本信息 | 设备编码、名称、类型、地址等 |
| `acs_device_extra` | 设备扩展属性 | 站点管理、自定义配置等 |
| `acs_opc` | OPC服务器配置 | 设备通信连接信息 |
| `acs_opc_plc` | PLC配置 | 设备PLC连接信息 |
### 1.3 设备驱动绑定机制
设备驱动采用 **策略模式**,通过 `DeviceDriverDefination` 动态创建驱动实例:
```java
// DeviceDriverDefination.getDriverInstance(device)
// 根据驱动定义,为每个设备创建对应的驱动实例
DeviceDriver driver = defination.getDriverInstance(device);
device.setDeviceDriver(driver);
```
## 2. 设备信息数据结构
### 2.1 Device 实体
```java
public class Device {
private String device_code; // 设备编码(唯一标识)
private String device_name; // 设备名称
private String device_type; // 设备类型
private String address; // 设备地址
private String driver_code; // 驱动编码
private String opc_server_id; // OPC服务器ID
private String opc_plc_id; // PLC ID
private DeviceDriver deviceDriver; // 设备驱动实例
private DeviceDriverDefination deviceDriverDefination; // 驱动定义
}
```
### 2.2 DeviceDriver 抽象类
```java
public abstract class DeviceDriver {
private Device device; // 关联设备
private String deviceCode; // 设备编码
private DeviceDriverDefination deviceDriverDefination; // 驱动定义
private boolean stop; // 停止标志
public DeviceDriver init(Device device, DeviceDriverDefination defination) {
this.device = device;
this.deviceCode = device.getDevice_code();
this.deviceDriverDefination = defination;
return this;
}
}
```
## 3. 设备信息使用方式
### 3.1 基础查询接口
```java
// DeviceAppService 提供的查询方法
// 1. 查询所有设备
List<Device> devices = deviceAppService.findAllDevice();
// 2. 根据设备编码查询
Device device = deviceAppService.findDeviceByCode("DEVICE001");
// 3. 根据设备地址查询
Device device = deviceAppService.findDeviceByAddress("A01-01");
// 4. 根据设备类型查询
List<Device> devices = deviceAppService.findDeviceByType(DeviceType.STANDARD_INSPECT);
// 5. 获取设备驱动列表(按类型筛选)
List<BaseDeviceDriver> drivers = deviceAppService.findDeviceDriver(BaseDeviceDriver.class);
```
### 3.2 设备驱动调用
通过设备驱动执行具体业务操作:
```java
// 获取设备驱动
Device device = deviceAppService.findDeviceByCode("DEVICE001");
DeviceDriver driver = device.getDeviceDriver();
// 执行设备操作(具体方法由子类实现)
if (driver instanceof BaseDeviceDriver) {
BaseDeviceDriver baseDriver = (BaseDeviceDriver) driver;
baseDriver.executeAuto(); // 执行自动任务
}
```
### 3.3 自动线程调用
`DeviceExecuteAutoRunable` 自动线程定期执行设备任务:
```java
// DeviceExecuteAutoRunable.autoRun()
public void autoRun() throws Exception {
// 等待OPC同步完成最多60秒
for (int i = 0; !OpcConfig.OPC_START_TAG; ++i) {
Thread.sleep(1000L);
if (i > 60) break;
}
// 循环执行设备任务
while (true) {
Thread.sleep(100);
List<BaseDeviceDriver> deviceDrivers = deviceAppService.findDeviceDriver(BaseDeviceDriver.class);
for (BaseDeviceDriver driver : deviceDrivers) {
// 提交到线程池执行
executorService.submit(() -> driver.executeAuto());
}
}
}
```
### 3.4 设备驱动插件体系
设备驱动采用 **插件模式**,实现两个核心接口:
| 接口 | 职责 | 核心方法 |
|------|------|----------|
| `DriverExecutePlugin` | 设备执行逻辑 | `execute()` - 执行具体业务 |
| `DeviceStageMonitorPlugin` | 设备状态监控 | `getDeviceStatusName()` - 获取状态 |
```java
// DriverExecutePlugin 接口
public interface DriverExecutePlugin {
default void executeAuto() {
try {
this.execute(); // 调用子类实现
} catch (Exception ex) {
ex.printStackTrace();
}
}
void execute() throws Exception; // 子类必须实现
}
// DeviceStageMonitorPlugin 接口
public interface DeviceStageMonitorPlugin {
JSONObject getDeviceStatusName() throws Exception; // 获取设备状态
void setDeviceStatus(JSONObject data); // 设置设备状态
}
```
### 3.5 OPC设备驱动使用
`AbstractOpcDeviceDriver` 是 OPC 设备驱动的基类,提供以下核心能力:
#### ① OPC信号读取
```java
// 获取设备驱动
Device device = deviceAppService.findDeviceByCode("DEVICE001");
AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) device.getDeviceDriver();
// 读取单个OPC信号值
Integer value = opcDriver.getIntegeregerValue("heartbeat"); // 获取心跳值
Object rawValue = opcDriver.getValue("running"); // 获取原始值
// 获取所有OPC点位配置
List<OpcItemDto> opcItems = opcDriver.getOpcItems();
for (OpcItemDto item : opcItems) {
String itemCode = item.getItem_code();
String deviceCode = item.getDevice_code();
// 处理OPC点位
}
```
#### ② OPC信号写入
```java
// 构建要写入的信号数据
Map<String, Object> itemValues = new HashMap<>();
itemValues.put("opc_server.plc.device001.start", 1); // 启动信号
itemValues.put("opc_server.plc.device001.speed", 100); // 速度设置
// 调用控制方法(带重试机制)
opcDriver.checkcontrol(itemValues);
// 或者直接调用control方法
boolean result = opcDriver.control(itemValues);
if (!result) {
throw new RuntimeException("OPC写入失败");
}
```
#### ③ 获取设备扩展信息
```java
// 获取设备扩展配置
Map<String, Object> extraValues = opcDriver.getExtraValue();
String stationManager = (String) extraValues.get("station_manager");
// 获取设备连接信息
String opcServer = opcDriver.getOpcServer(); // OPC服务器编码
String opcPlc = opcDriver.getOpcPlc(); // PLC编码
String deviceCode = opcDriver.getDeviceCode(); // 设备编码
```
### 3.6 驱动执行上下文
每个驱动实例持有设备信息和统一数据访问器:
```java
// 获取关联设备
Device device = driver.getDevice();
// 获取驱动定义
DeviceDriverDefination defination = driver.getDriverDefination();
// 获取统一数据访问器用于读写OPC数据
UnifiedDataAccessor opcUdw = opcDriver.getOpcValueAccessor();
Object value = opcUdw.getValue("opc_server.plc.device001.status");
```
## 4. 业务场景示例
### 4.1 场景一:设备状态监控
```java
// 查询所有设备状态
List<Device> devices = deviceAppService.findAllDevice();
for (Device device : devices) {
DeviceDriver driver = device.getDeviceDriver();
if (driver instanceof AbstractOpcDeviceDriver) {
AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) driver;
List<OpcItemDto> opcItems = opcDriver.getOpcItems();
// 处理OPC信号数据
}
// 获取设备状态名称(故障、联机等)
if (driver instanceof DeviceStageMonitorPlugin) {
DeviceStageMonitorPlugin monitor = (DeviceStageMonitorPlugin) driver;
JSONObject status = monitor.getDeviceStatusName();
String statusName = status.getString("statusName");
System.out.println("设备 " + device.getDevice_code() + " 状态: " + statusName);
}
}
```
### 4.2 场景二:设备指令执行
```java
// 根据设备编码执行指令
String deviceCode = "CONVEYOR001";
Device device = deviceAppService.findDeviceByCode(deviceCode);
if (device != null && device.getDeviceDriver() instanceof BaseDeviceDriver) {
BaseDeviceDriver driver = (BaseDeviceDriver) device.getDeviceDriver();
driver.executeAuto(); // 执行设备自动任务
}
```
### 4.3 场景三:设备动态管理
```java
// 运行时添加设备
deviceAppService.addDevice("NEW_DEVICE_001");
// 运行时移除设备
deviceAppService.removeDevice("DEVICE_TO_REMOVE");
// 重新加载所有设备
deviceAppService.reload();
```
### 4.4 场景四OPC信号批量写入
```java
// 批量控制多个设备
List<String> deviceCodes = Arrays.asList("CONVEYOR001", "CONVEYOR002", "CONVEYOR003");
for (String deviceCode : deviceCodes) {
Device device = deviceAppService.findDeviceByCode(deviceCode);
if (device != null && device.getDeviceDriver() instanceof AbstractOpcDeviceDriver) {
AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) device.getDeviceDriver();
// 构建控制信号
Map<String, Object> signals = new HashMap<>();
signals.put(opcDriver.getItem("start"), 1);
signals.put(opcDriver.getItem("speed"), 50);
// 执行写入(带重试机制)
try {
opcDriver.checkcontrol(signals);
log.info("设备 {} 控制成功", deviceCode);
} catch (Exception e) {
log.error("设备 {} 控制失败: {}", deviceCode, e.getMessage());
}
}
}
```
### 4.5 场景五:设备状态同步
```java
// 从OPC读取设备状态并更新到系统
String deviceCode = "LIFT001";
Device device = deviceAppService.findDeviceByCode(deviceCode);
if (device != null && device.getDeviceDriver() instanceof AbstractOpcDeviceDriver) {
AbstractOpcDeviceDriver opcDriver = (AbstractOpcDeviceDriver) device.getDeviceDriver();
// 读取多个信号
Integer heartbeat = opcDriver.getIntegeregerValue("heartbeat");
Integer running = opcDriver.getIntegeregerValue("running");
Integer fault = opcDriver.getIntegeregerValue("fault");
// 构建状态数据
JSONObject statusData = new JSONObject();
statusData.put("deviceCode", deviceCode);
statusData.put("heartbeat", heartbeat);
statusData.put("running", running == 1);
statusData.put("fault", fault == 1);
// 更新设备状态
DeviceStageMonitorPlugin monitor = (DeviceStageMonitorPlugin) opcDriver;
monitor.setDeviceStatus(statusData);
// 存储到数据库或缓存
deviceStatusService.updateStatus(deviceCode, statusData);
}
```
### 4.6 场景六:自定义驱动执行
```java
// 创建自定义任务执行器
public class CustomDeviceTask implements Runnable {
private BaseDeviceDriver driver;
public CustomDeviceTask(BaseDeviceDriver driver) {
this.driver = driver;
}
@Override
public void run() {
try {
// 执行前置检查
if (!checkDeviceReady(driver)) {
log.warn("设备 {} 未就绪", driver.getDeviceCode());
return;
}
// 执行驱动逻辑
driver.execute();
// 记录执行日志
log.info("设备 {} 任务执行完成", driver.getDeviceCode());
} catch (Exception e) {
log.error("设备 {} 任务执行失败: {}", driver.getDeviceCode(), e.getMessage());
}
}
private boolean checkDeviceReady(BaseDeviceDriver driver) {
// 检查设备状态、连接状态等
return true;
}
}
// 使用示例
BaseDeviceDriver driver = deviceAppService.findDeviceDriver("CONVEYOR001");
if (driver != null) {
executorService.submit(new CustomDeviceTask(driver));
}
```
## 5. 关键设计要点
### 5.1 内存缓存策略
| 设计点 | 说明 |
|--------|------|
| **缓存时机** | 应用启动时加载,运行时动态更新 |
| **缓存结构** | `devices` 列表 + `code_indexs` 索引 |
| **线程安全** | 使用 `synchronized``Collections.synchronizedList()` |
| **更新机制** | 提供 `reload()``addDevice()``removeDevice()` 方法 |
### 5.2 驱动解耦设计
- **策略模式**:通过 `DeviceDriverDefination` 解耦驱动定义与实现
- **依赖注入**Spring 自动扫描驱动实现类
- **动态创建**:根据 `driver_code` 动态创建对应驱动实例
### 5.3 配置校验
在加载设备时进行多重校验:
```java
// queryAllWithExtra() 中的校验逻辑
if (opc == null) {
log.info("设备:{},配置的OPC数据源不存在", device_code);
continue; // 跳过无效配置
}
if (opcPlc == null) {
log.info("设备:{},配置的PLC数据源不存在", device_code);
continue; // 跳过无效配置
}
```
## 6. 扩展开发指南
### 6.1 添加新设备类型
1. 在数据库中添加设备记录(`acs_device` 表)
2. 配置设备扩展信息(`acs_device_extra` 表)
3. 配置 OPC/PLC 连接信息(`acs_opc``acs_opc_plc` 表)
4. 确保对应的驱动已注册
### 6.2 添加新设备驱动
1. 继承 `DeviceDriver` 抽象类
2. 实现具体的业务逻辑方法
3.`DeviceDriverDefination` 中注册驱动
---
**文档版本**: v1.0
**创建日期**: 2026年4月
**适用模块**: NL Admin System - 设备管理模块

View File

@@ -1,7 +1,334 @@
### storageMgt
货位管理模块主要是AGV取放货的点
1.货位编码与上位系统保持一致
2.AGV地址有2个正常逻辑取放地址是同一个,如果取放货位不一样需要配合adress1共同使用
3.offset货位偏移量主要用于ACSKit套件对接AGV到达取放货位时会请求取放货这时通过offset偏移量来下发给kit
4.has:有无货状态AGV取放货完成后更新该状态
# 设备协议管理文档
## 1. 设备协议概述
设备协议模块负责管理设备与PLC之间的通信协议配置主要功能包括
- 设备协议信息查询
- 协议数据导出支持多种PLC类型
- 协议格式校验与转换
### 1.1 协议数据结构
设备协议数据存储在 `acs_device_extra` 表中,核心字段:
| 字段名 | 类型 | 说明 |
|--------|------|------|
| `device_id` | VARCHAR | 关联设备ID |
| `extra_code` | VARCHAR | 协议编码格式OPC_SERVER.PLC.DEVICE.CODE |
| `extra_name` | VARCHAR | 协议名称PLC地址如 DB100.B1 |
| `extra_value` | VARCHAR | 协议值 |
| `filed_type` | VARCHAR | 字段类型02-输入点位03-输出点位) |
### 1.2 协议编码格式
协议编码采用 **四级分层结构**
```
OPC_SERVER_CODE.PLC_CODE.DEVICE_CODE.POINT_CODE
↓ ↓ ↓ ↓
RD1 RD1 A1 mode
(OPC服务器) (PLC编码) (设备编码) (点位编码)
```
示例:`RD1.RD1.A1.mode`
---
## 2. 核心服务接口
### 2.1 DeviceProtocolService
```java
public interface DeviceProtocolService {
/**
* 分页查询设备协议
* @param whereJson 查询条件
* @param page 分页参数
* @return 分页结果
*/
Map<String, Object> queryDeviceProtocol(Map whereJson, Pageable page);
/**
* 查询设备协议列表(不分页)
* @param whereJson 查询条件
* @return 协议列表
*/
JSONArray queryDeviceProtocol(Map whereJson);
/**
* 导出设备DB协议Excel格式
* @param jsonarr 协议数据
* @param response HTTP响应
*/
void downDeviceDBload(JSONArray jsonarr, HttpServletResponse response) throws IOException;
/**
* 导出Smart设备协议CSV
* @param queryDeviceProtocol 协议数据
* @param response HTTP响应
*/
void downDeviceDBloadSmartCSV(JSONArray queryDeviceProtocol, HttpServletResponse response);
/**
* 导出FX5U设备协议CSV
* @param queryDeviceProtocol 协议数据
* @param response HTTP响应
*/
void downDeviceDBloadFX5UCSV(JSONArray queryDeviceProtocol, HttpServletResponse response);
/**
* 导出通用CSV西门子1200/1500
* @param queryDeviceProtocol 协议数据
* @param response HTTP响应
*/
void downDeviceDBloadCSV(JSONArray queryDeviceProtocol, HttpServletResponse response);
}
```
### 2.2 查询条件说明
| 参数名 | 类型 | 说明 |
|--------|------|------|
| `device_types` | String | 设备类型筛选 |
| `blurry` | String | 模糊搜索(设备编码或名称) |
| `opc_id` | String | OPC服务器ID筛选 |
---
## 3. 协议导出功能
### 3.1 支持的PLC类型
| PLC类型 | 导出方法 | 数据类型映射 |
|---------|----------|--------------|
| **西门子200 Smart** | `downDeviceDBloadSmartCSV()` | W→Word, D→DWord, 含"."→Boolean |
| **三菱FX5U** | `downDeviceDBloadFX5UCSV()` | D→Long, 含"."→Boolean |
| **西门子1200/1500** | `downDeviceDBloadCSV()` | DBx.By→Byte/Boolean, W→Word, D→DWord |
### 3.2 CSV导出格式
导出的CSV文件包含以下列
| 列名 | 说明 | 示例 |
|------|------|------|
| **Tag Name** | 点位编码 | `mode` |
| **Address** | PLC地址 | `DB100.B1.0` |
| **Data Type** | 数据类型 | `Boolean` / `Word` / `DWord` |
| **Respect Data Type** | 保留字段 | `1` |
| **Client Access** | 访问权限 | `R/W` |
| **Scan Rate** | 扫描速率(ms) | `100` |
### 3.3 协议格式校验
在导出前会进行格式校验:
```java
// 校验协议编码格式必须包含3个点
int num = countStr(extra_code, ".");
if (num != 3) {
throw new BadRequestException("协议编码格式不正确: " + extra_code);
}
```
---
## 4. 数据类型映射规则
### 4.1 西门子200 Smart 映射规则
```java
String datatype = jo.getString("extra_name");
if (!datatype.contains(".")) {
String[] split = datatype.split("");
if (split[1].equals("W")) {
datatype = "Word"; // 如 VW100 → Word
} else if (split[1].equals("D")) {
datatype = "DWord"; // 如 VD100 → DWord
}
} else {
datatype = "Boolean"; // 如 I0.0 → Boolean
}
```
### 4.2 三菱FX5U 映射规则
```java
if (!datatype.contains(".")) {
String[] split = datatype.split("");
if (split[1].equals("D")) {
datatype = "Long"; // 如 D100 → Long
}
} else {
datatype = "Boolean"; // 如 X0.0 → Boolean
}
```
### 4.3 西门子1200/1500 映射规则
```java
if (datatype.startsWith("DB")) {
String str1 = datatype.substring(0, datatype.indexOf("."));
String headtype = datatype.substring(str1.length() + 1);
if (headtype.startsWith("B")) {
if (headtype.contains(".")) {
datatype = "Boolean"; // DB100.B1.0 → Boolean
} else {
datatype = "Byte"; // DB100.B1 → Byte
}
} else if (headtype.startsWith("W")) {
datatype = "Word"; // DB100.W1 → Word
} else if (headtype.startsWith("D")) {
datatype = "DWord"; // DB100.D1 → DWord
} else if (headtype.startsWith("S")) {
datatype = "String"; // DB100.S1 → String
} else if (headtype.startsWith("REAL")) {
datatype = "REAL"; // DB100.REAL1 → REAL
}
}
```
---
## 5. 使用示例
### 5.1 查询设备协议
```java
// 构建查询条件
Map<String, Object> whereJson = new HashMap<>();
whereJson.put("device_types", "CONVEYOR"); // 筛选设备类型
whereJson.put("blurry", "A1"); // 模糊搜索
whereJson.put("opc_id", "OPC_SERVER_01"); // 筛选OPC服务器
// 分页查询
Pageable page = PageRequest.of(0, 10);
Map<String, Object> result = deviceProtocolService.queryDeviceProtocol(whereJson, page);
// 不分页查询
JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson);
```
### 5.2 导出协议CSV
```java
// 查询协议数据
JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson);
// 导出西门子1200/1500格式
deviceProtocolService.downDeviceDBloadCSV(protocols, response);
// 或者导出西门子200 Smart格式
deviceProtocolService.downDeviceDBloadSmartCSV(protocols, response);
// 或者导出三菱FX5U格式
deviceProtocolService.downDeviceDBloadFX5UCSV(protocols, response);
```
### 5.3 导出协议Excel
```java
JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson);
deviceProtocolService.downDeviceDBload(protocols, response);
```
---
## 6. 业务场景
### 6.1 场景一批量导出PLC配置
```java
// 查询所有设备协议
Map<String, Object> whereJson = new HashMap<>();
JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson);
// 根据PLC类型选择导出方法
String plcType = "SIEMENS_1200";
switch (plcType) {
case "SIEMENS_200SMART":
deviceProtocolService.downDeviceDBloadSmartCSV(protocols, response);
break;
case "MITSUBISHI_FX5U":
deviceProtocolService.downDeviceDBloadFX5UCSV(protocols, response);
break;
case "SIEMENS_1200":
case "SIEMENS_1500":
default:
deviceProtocolService.downDeviceDBloadCSV(protocols, response);
break;
}
```
### 6.2 场景二:按设备类型导出
```java
// 只导出特定类型设备的协议
Map<String, Object> whereJson = new HashMap<>();
whereJson.put("device_types", "LIFT"); // 只导出提升机设备
JSONArray protocols = deviceProtocolService.queryDeviceProtocol(whereJson);
deviceProtocolService.downDeviceDBloadCSV(protocols, response);
```
### 6.3 场景三前端API调用
```javascript
// 查询协议列表
fetch('/api/deviceProtocol', {
method: 'GET',
params: {
device_types: 'CONVEYOR',
blurry: 'A1'
}
}).then(res => res.json())
.then(data => console.log(data));
// 导出CSV
window.open('/api/deviceProtocol/export?plcType=SIEMENS_1200');
```
---
## 7. 关键设计要点
### 7.1 协议编码规范
协议编码必须遵循以下格式:
```
OPC_SERVER.PLC.DEVICE.POINT
```
**示例**
- `RD1.RD1.A1.mode` - A1设备的mode点位
- `OPC_SRV01.PLC01.CONV001.start` - CONV001设备的start点位
### 7.2 数据类型自动识别
系统根据协议名称自动识别数据类型:
| 协议名称特征 | 识别的数据类型 |
|--------------|----------------|
| 包含 `.` 且以数字结尾 | Boolean如 DB100.B1.0 |
| 以 `W` 开头(不含`.` | Word如 VW100 |
| 以 `D` 开头(不含`.` | DWord 或 Long |
| 以 `DB` 开头 | 根据后续字符判断 |
### 7.3 协议分类
根据 `filed_type` 字段区分协议类型:
| filed_type | 含义 | 用途 |
|------------|------|------|
| `01` | 设备属性 | 设备基础配置(不参与协议导出) |
| `02` | 输入点位 | PLC输入信号如传感器状态 |
| `03` | 输出点位 | PLC输出信号如控制指令 |
---
**文档版本**: v1.0
**创建日期**: 2026年4月
**适用模块**: NL Admin System - 设备协议模块

View File

@@ -11,10 +11,10 @@
加载所有设备信息devices至本地内存
#### 3.StorageCellServiceImpl
加载货位信息至内存
加载货位信息至内存
#### NDCAutoRun
NDC自动线程通过该线程与NDC创建socket通讯做phace逻辑交互
该版本废弃统一改agvKit交互
NDC自动线程通过该线程与NDC创建socket通讯做phace逻辑交互
该版本废弃统一改agvKit交互
#### DeviceOpcSynchronizeAutoRun
同步所有OpcService信号
该版本固定使用KEP-SERVER 进行信号同步