fix:协议驱动完善+工厂类

This commit is contained in:
2026-03-19 13:55:18 +08:00
parent 29b5a0fb82
commit c1696110a9
9 changed files with 310 additions and 336 deletions

View File

@@ -2,121 +2,134 @@ package org.nl.iot.core.driver;
import lombok.extern.slf4j.Slf4j;
import org.nl.common.exception.CommonException;
import org.nl.iot.core.driver.protocol.modbustcp.ModBusProtocolDriverImpl;
import org.nl.iot.core.driver.protocol.opcda.OpcDaProtocolDriverImpl;
import org.nl.iot.core.driver.protocol.opcua.OpcUaProtocolDriverImpl;
import org.nl.iot.core.driver.protocol.plcs7.PlcS7ProtocolDriverImpl;
import org.nl.iot.core.driver.service.DriverCustomService;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
/**
* 自定义通信协议驱动工厂
* 通过协议编码获取对应的协议驱动单例对象
*
* <p>重要说明:
* <ul>
* <li>工厂返回的驱动实例是 Spring 容器中的 Bean 对象(单例)</li>
* <li>所有驱动类都标注了 @Service 注解,由 Spring 管理生命周期</li>
* <li>工厂通过构造函数注入获取这些 Bean并直接返回引用</li>
* <li>多次调用 getDriver() 返回的是同一个 Spring Bean 实例</li>
* </ul>
*
*
* @author: lyd
* @date: 2026/3/2
*/
@Slf4j
@Component
public class DriverCustomFactory {
public class DriverCustomFactory implements InitializingBean {
private final ModBusProtocolDriverImpl modBusProtocolDriver;
private final OpcUaProtocolDriverImpl opcUaProtocolDriver;
private final PlcS7ProtocolDriverImpl plcS7ProtocolDriver;
private final OpcDaProtocolDriverImpl opcDaProtocolDriver;
private final ApplicationContext applicationContext;
/**
* 协议驱动缓存Map
* key: 协议编码MODBUS、OPCUA、PLCS7、OPCDA
* value: 对应的协议驱动实例
* 协议驱动缓存Map(主编码+兼容编码 → 驱动实例)
*/
private final Map<String, DriverCustomService> driverMap = new ConcurrentHashMap<>();
/**
* 构造函数注入所有协议驱动实例
* 仅注入ApplicationContext无需手动注入所有驱动
*/
public DriverCustomFactory(ModBusProtocolDriverImpl modBusProtocolDriver,
OpcUaProtocolDriverImpl opcUaProtocolDriver,
PlcS7ProtocolDriverImpl plcS7ProtocolDriver,
OpcDaProtocolDriverImpl opcDaProtocolDriver) {
this.modBusProtocolDriver = modBusProtocolDriver;
this.opcUaProtocolDriver = opcUaProtocolDriver;
this.plcS7ProtocolDriver = plcS7ProtocolDriver;
this.opcDaProtocolDriver = opcDaProtocolDriver;
// 初始化协议驱动映射
initDriverMap();
public DriverCustomFactory(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
/**
* 初始化协议驱动映射关系
* 初始化:自动扫描所有带@ProtocolDriver注解的驱动Bean
*/
private void initDriverMap() {
driverMap.put("MODBUS", modBusProtocolDriver);
driverMap.put("MODBUS-TCP", modBusProtocolDriver);
driverMap.put("OPCUA", opcUaProtocolDriver);
driverMap.put("OPC-UA", opcUaProtocolDriver);
driverMap.put("PLCS7", plcS7ProtocolDriver);
driverMap.put("PLC-S7", plcS7ProtocolDriver);
driverMap.put("OPCDA", opcDaProtocolDriver);
driverMap.put("OPC-DA", opcDaProtocolDriver);
log.info("协议驱动工厂初始化完成,支持的协议: {}", driverMap.keySet());
@Override
public void afterPropertiesSet() {
// 扫描Spring容器中所有实现DriverCustomService且带ProtocolDriver注解的Bean
Map<String, DriverCustomService> driverBeans = applicationContext.getBeansOfType(DriverCustomService.class);
for (DriverCustomService driver : driverBeans.values()) {
ProtocolDriver annotation = driver.getClass().getAnnotation(ProtocolDriver.class);
if (annotation == null) {
log.warn("驱动类{}未标注@ProtocolDriver注解将被忽略", driver.getClass().getSimpleName());
continue;
}
// 获取协议类型
ProtocolType protocolType = annotation.value();
// 注册主编码
driverMap.put(protocolType.getMainCode(), driver);
// 注册兼容编码
for (String compatibleCode : protocolType.getCompatibleCodes()) {
driverMap.put(compatibleCode, driver);
}
log.debug("注册协议驱动: {} → {}", protocolType.getMainCode(), driver.getClass().getSimpleName());
}
log.info("协议驱动工厂初始化完成,支持的协议编码: {}", driverMap.keySet());
}
/**
* 根据协议编码获取对应的协议驱动单例对象
*
* <p>返回的驱动实例是 Spring 容器中的 Bean 对象(单例):
* <ul>
* <li>工厂不会创建新对象,只是返回已注入的 Spring Bean 引用</li>
* <li>多次调用返回的是同一个对象(使用 == 比较为 true</li>
* <li>与直接 @Autowired 注入的驱动 Bean 是同一个实例</li>
* </ul>
*
* @param protocolCode 协议编码MODBUS、OPCUA、PLCS7、OPCDA不区分大小写
* @return 对应的协议驱动实例Spring Bean 单例对象)
* @throws CommonException 如果协议编码不支持,抛出异常
* 优化点:
* 1. 基于枚举匹配,减少硬编码
* 2. 异常信息更友好,包含支持的协议列表
* 3. 日志输出更规范
*/
public DriverCustomService getDriver(String protocolCode) {
// 空值校验
if (protocolCode == null || protocolCode.trim().isEmpty()) {
throw new CommonException("协议编码不能为空");
String errorMsg = "协议编码不能为空";
log.error(errorMsg);
throw new CommonException(errorMsg);
}
// 转换为大写进行匹配
String upperProtocolCode = protocolCode.trim().toUpperCase();
DriverCustomService driver = driverMap.get(upperProtocolCode);
if (driver == null) {
log.error("不支持的协议编码: {}", protocolCode);
throw new CommonException("不支持的协议编码: " + protocolCode + ",支持的协议: " + driverMap.keySet());
// 匹配协议
ProtocolType protocolType = ProtocolType.matchByCode(protocolCode);
if (protocolType == null) {
String errorMsg = String.format("不支持的协议编码: %s支持的协议编码: %s",
protocolCode, ProtocolType.getAllSupportedCodes());
log.error(errorMsg);
throw new CommonException(errorMsg);
}
log.debug("获取协议驱动: {} -> {}", protocolCode, driver.getClass().getSimpleName());
// 获取驱动实例
String upperCode = protocolCode.trim().toUpperCase();
DriverCustomService driver = driverMap.get(upperCode);
log.debug("成功获取协议驱动:请求编码={},匹配协议类型={},驱动类={}",
protocolCode, protocolType.name(), driver.getClass().getSimpleName());
return driver;
}
/**
* 检查是否支持指定的协议编码
*
* @param protocolCode 协议编码
* @return 如果支持返回true否则返回false
* 优化点:基于枚举匹配,逻辑更统一
*/
public boolean isSupported(String protocolCode) {
if (protocolCode == null || protocolCode.trim().isEmpty()) {
return false;
}
return driverMap.containsKey(protocolCode.trim().toUpperCase());
return ProtocolType.matchByCode(protocolCode) != null;
}
}
/**
* 扩展方法:动态注册驱动(支持运行时新增驱动)
*/
public void registerDriver(ProtocolType protocolType, DriverCustomService driver) {
if (protocolType == null || driver == null) {
throw new CommonException("协议类型和驱动实例不能为空");
}
// 注册主编码和兼容编码
driverMap.put(protocolType.getMainCode(), driver);
for (String compatibleCode : protocolType.getCompatibleCodes()) {
driverMap.put(compatibleCode, driver);
}
log.info("动态注册协议驱动: {} → {}", protocolType.getMainCode(), driver.getClass().getSimpleName());
}
/**
* 扩展方法:获取所有支持的协议类型
*/
public Set<ProtocolType> getSupportedProtocolTypes() {
return new HashSet<>(Arrays.asList(ProtocolType.values()));
}
}

View File

@@ -0,0 +1,16 @@
package org.nl.iot.core.driver;
import java.lang.annotation.*;
/**
* 协议驱动注解:标记驱动类对应的协议类型
* @Author: liyongde
* @Date: 2026/3/19 13:39
* @Modified By:
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ProtocolDriver {
ProtocolType value();
}

View File

@@ -0,0 +1,63 @@
package org.nl.iot.core.driver;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* 协议编码枚举(统一管理支持的协议)
* 增协议只需在此枚举中添加,无需修改工厂类
* @Author: liyongde
* @Date: 2026/3/19 13:30
* @Modified By:
*/
@Getter
@AllArgsConstructor
public enum ProtocolType {
MODBUS("MODBUS", "MODBUS-TCP"),
OPCUA("OPCUA", "OPC-UA"),
PLCS7("PLCS7", "PLC-S7"),
OPCDA("OPCDA", "OPC-DA");
// 主协议编码
private final String mainCode;
// 兼容的协议编码
private final String[] compatibleCodes;
// 构造函数适配单兼容码场景
ProtocolType(String mainCode, String compatibleCode) {
this.mainCode = mainCode;
this.compatibleCodes = new String[]{compatibleCode};
}
/**
* 根据编码匹配协议枚举(不区分大小写)
*/
public static ProtocolType matchByCode(String code) {
if (code == null || code.trim().isEmpty()) {
return null;
}
String upperCode = code.trim().toUpperCase();
for (ProtocolType type : values()) {
if (type.getMainCode().equals(upperCode)) {
return type;
}
for (String compatibleCode : type.getCompatibleCodes()) {
if (compatibleCode.equals(upperCode)) {
return type;
}
}
}
return null;
}
/**
* 获取所有支持的协议编码(主编码+兼容编码)
*/
public static String getAllSupportedCodes() {
StringBuilder sb = new StringBuilder();
for (ProtocolType type : values()) {
sb.append(type.getMainCode()).append(",").append(String.join(",", type.getCompatibleCodes())).append(",");
}
return sb.toString().replaceAll(",$", "");
}
}

View File

@@ -11,6 +11,8 @@ import org.apache.plc4x.java.api.messages.PlcWriteRequest;
import org.apache.plc4x.java.api.messages.PlcWriteResponse;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.api.value.PlcValue;
import org.nl.iot.core.driver.ProtocolDriver;
import org.nl.iot.core.driver.ProtocolType;
import org.nl.iot.core.driver.bo.DeviceBO;
import org.nl.iot.core.driver.bo.MetadataEventDTO;
import org.nl.iot.core.driver.bo.SiteBO;
@@ -36,6 +38,7 @@ import java.util.concurrent.TimeUnit;
*/
@Slf4j
@Service
@ProtocolDriver(ProtocolType.MODBUS)
public class ModBusProtocolDriverImpl implements DriverCustomService {
private Map<String, PlcConnection> connectMap;

View File

@@ -8,6 +8,8 @@ import lombok.extern.slf4j.Slf4j;
import org.jinterop.dcom.common.JIException;
import org.jinterop.dcom.core.JIVariant;
import org.nl.common.exception.CommonException;
import org.nl.iot.core.driver.ProtocolDriver;
import org.nl.iot.core.driver.ProtocolType;
import org.nl.iot.core.driver.bo.DeviceBO;
import org.nl.iot.core.driver.bo.MetadataEventDTO;
import org.nl.iot.core.driver.bo.SiteBO;
@@ -34,6 +36,7 @@ import java.util.concurrent.Executors;
@Slf4j
@Service
@ProtocolDriver(ProtocolType.OPCDA)
public class OpcDaProtocolDriverImpl implements DriverCustomService {
/**
* Opc Da Server Map

View File

@@ -15,6 +15,8 @@ import org.eclipse.milo.opcua.stack.core.types.builtin.unsigned.Unsigned;
import org.eclipse.milo.opcua.stack.core.types.enumerated.TimestampsToReturn;
import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
import org.nl.common.exception.CommonException;
import org.nl.iot.core.driver.ProtocolDriver;
import org.nl.iot.core.driver.ProtocolType;
import org.nl.iot.core.driver.bo.DeviceBO;
import org.nl.iot.core.driver.bo.MetadataEventDTO;
import org.nl.iot.core.driver.bo.SiteBO;
@@ -35,6 +37,7 @@ import java.util.concurrent.*;
@Slf4j
@Service
@ProtocolDriver(ProtocolType.OPCUA)
public class OpcUaProtocolDriverImpl implements DriverCustomService {
private Map<String, OpcUaClient> connectMap;

View File

@@ -11,6 +11,8 @@ import org.apache.plc4x.java.api.messages.PlcWriteResponse;
import org.apache.plc4x.java.api.types.PlcResponseCode;
import org.apache.plc4x.java.api.value.PlcValue;
import org.nl.common.exception.CommonException;
import org.nl.iot.core.driver.ProtocolDriver;
import org.nl.iot.core.driver.ProtocolType;
import org.nl.iot.core.driver.bo.DeviceBO;
import org.nl.iot.core.driver.bo.MetadataEventDTO;
import org.nl.iot.core.driver.bo.SiteBO;
@@ -34,6 +36,7 @@ import java.util.concurrent.*;
*/
@Slf4j
@Service
@ProtocolDriver(ProtocolType.PLCS7)
public class PlcS7ProtocolDriverImpl implements DriverCustomService {
/**