fix:协议驱动完善+工厂类
This commit is contained in:
@@ -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()));
|
||||
}
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
@@ -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(",$", "");
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user