fix:协议驱动完善+工厂类
This commit is contained in:
@@ -2,121 +2,134 @@ package org.nl.iot.core.driver;
|
|||||||
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.nl.common.exception.CommonException;
|
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.nl.iot.core.driver.service.DriverCustomService;
|
||||||
|
import org.springframework.beans.factory.InitializingBean;
|
||||||
|
import org.springframework.context.ApplicationContext;
|
||||||
import org.springframework.stereotype.Component;
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
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
|
* @author: lyd
|
||||||
* @date: 2026/3/2
|
* @date: 2026/3/2
|
||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Component
|
@Component
|
||||||
public class DriverCustomFactory {
|
public class DriverCustomFactory implements InitializingBean {
|
||||||
|
|
||||||
private final ModBusProtocolDriverImpl modBusProtocolDriver;
|
private final ApplicationContext applicationContext;
|
||||||
private final OpcUaProtocolDriverImpl opcUaProtocolDriver;
|
|
||||||
private final PlcS7ProtocolDriverImpl plcS7ProtocolDriver;
|
|
||||||
private final OpcDaProtocolDriverImpl opcDaProtocolDriver;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 协议驱动缓存Map
|
* 协议驱动缓存Map(主编码+兼容编码 → 驱动实例)
|
||||||
* key: 协议编码(如:MODBUS、OPCUA、PLCS7、OPCDA)
|
|
||||||
* value: 对应的协议驱动实例
|
|
||||||
*/
|
*/
|
||||||
private final Map<String, DriverCustomService> driverMap = new ConcurrentHashMap<>();
|
private final Map<String, DriverCustomService> driverMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 构造函数注入所有协议驱动实例
|
* 仅注入ApplicationContext,无需手动注入所有驱动
|
||||||
*/
|
*/
|
||||||
public DriverCustomFactory(ModBusProtocolDriverImpl modBusProtocolDriver,
|
public DriverCustomFactory(ApplicationContext applicationContext) {
|
||||||
OpcUaProtocolDriverImpl opcUaProtocolDriver,
|
this.applicationContext = applicationContext;
|
||||||
PlcS7ProtocolDriverImpl plcS7ProtocolDriver,
|
|
||||||
OpcDaProtocolDriverImpl opcDaProtocolDriver) {
|
|
||||||
this.modBusProtocolDriver = modBusProtocolDriver;
|
|
||||||
this.opcUaProtocolDriver = opcUaProtocolDriver;
|
|
||||||
this.plcS7ProtocolDriver = plcS7ProtocolDriver;
|
|
||||||
this.opcDaProtocolDriver = opcDaProtocolDriver;
|
|
||||||
|
|
||||||
// 初始化协议驱动映射
|
|
||||||
initDriverMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化协议驱动映射关系
|
* 初始化:自动扫描所有带@ProtocolDriver注解的驱动Bean
|
||||||
*/
|
*/
|
||||||
private void initDriverMap() {
|
@Override
|
||||||
driverMap.put("MODBUS", modBusProtocolDriver);
|
public void afterPropertiesSet() {
|
||||||
driverMap.put("MODBUS-TCP", modBusProtocolDriver);
|
// 扫描Spring容器中所有实现DriverCustomService且带ProtocolDriver注解的Bean
|
||||||
driverMap.put("OPCUA", opcUaProtocolDriver);
|
Map<String, DriverCustomService> driverBeans = applicationContext.getBeansOfType(DriverCustomService.class);
|
||||||
driverMap.put("OPC-UA", opcUaProtocolDriver);
|
|
||||||
driverMap.put("PLCS7", plcS7ProtocolDriver);
|
for (DriverCustomService driver : driverBeans.values()) {
|
||||||
driverMap.put("PLC-S7", plcS7ProtocolDriver);
|
ProtocolDriver annotation = driver.getClass().getAnnotation(ProtocolDriver.class);
|
||||||
driverMap.put("OPCDA", opcDaProtocolDriver);
|
if (annotation == null) {
|
||||||
driverMap.put("OPC-DA", opcDaProtocolDriver);
|
log.warn("驱动类{}未标注@ProtocolDriver注解,将被忽略", driver.getClass().getSimpleName());
|
||||||
|
continue;
|
||||||
log.info("协议驱动工厂初始化完成,支持的协议: {}", driverMap.keySet());
|
}
|
||||||
|
|
||||||
|
// 获取协议类型
|
||||||
|
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 对象(单例):
|
* 1. 基于枚举匹配,减少硬编码
|
||||||
* <ul>
|
* 2. 异常信息更友好,包含支持的协议列表
|
||||||
* <li>工厂不会创建新对象,只是返回已注入的 Spring Bean 引用</li>
|
* 3. 日志输出更规范
|
||||||
* <li>多次调用返回的是同一个对象(使用 == 比较为 true)</li>
|
|
||||||
* <li>与直接 @Autowired 注入的驱动 Bean 是同一个实例</li>
|
|
||||||
* </ul>
|
|
||||||
*
|
|
||||||
* @param protocolCode 协议编码(如:MODBUS、OPCUA、PLCS7、OPCDA),不区分大小写
|
|
||||||
* @return 对应的协议驱动实例(Spring Bean 单例对象)
|
|
||||||
* @throws CommonException 如果协议编码不支持,抛出异常
|
|
||||||
*/
|
*/
|
||||||
public DriverCustomService getDriver(String protocolCode) {
|
public DriverCustomService getDriver(String protocolCode) {
|
||||||
|
// 空值校验
|
||||||
if (protocolCode == null || protocolCode.trim().isEmpty()) {
|
if (protocolCode == null || protocolCode.trim().isEmpty()) {
|
||||||
throw new CommonException("协议编码不能为空");
|
String errorMsg = "协议编码不能为空";
|
||||||
|
log.error(errorMsg);
|
||||||
|
throw new CommonException(errorMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 转换为大写进行匹配
|
// 匹配协议
|
||||||
String upperProtocolCode = protocolCode.trim().toUpperCase();
|
ProtocolType protocolType = ProtocolType.matchByCode(protocolCode);
|
||||||
DriverCustomService driver = driverMap.get(upperProtocolCode);
|
if (protocolType == null) {
|
||||||
|
String errorMsg = String.format("不支持的协议编码: %s,支持的协议编码: %s",
|
||||||
if (driver == null) {
|
protocolCode, ProtocolType.getAllSupportedCodes());
|
||||||
log.error("不支持的协议编码: {}", protocolCode);
|
log.error(errorMsg);
|
||||||
throw new CommonException("不支持的协议编码: " + protocolCode + ",支持的协议: " + driverMap.keySet());
|
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;
|
return driver;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 检查是否支持指定的协议编码
|
* 检查是否支持指定的协议编码
|
||||||
*
|
* 优化点:基于枚举匹配,逻辑更统一
|
||||||
* @param protocolCode 协议编码
|
|
||||||
* @return 如果支持返回true,否则返回false
|
|
||||||
*/
|
*/
|
||||||
public boolean isSupported(String protocolCode) {
|
public boolean isSupported(String protocolCode) {
|
||||||
if (protocolCode == null || protocolCode.trim().isEmpty()) {
|
return ProtocolType.matchByCode(protocolCode) != null;
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return driverMap.containsKey(protocolCode.trim().toUpperCase());
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
/**
|
||||||
|
* 扩展方法:动态注册驱动(支持运行时新增驱动)
|
||||||
|
*/
|
||||||
|
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.messages.PlcWriteResponse;
|
||||||
import org.apache.plc4x.java.api.types.PlcResponseCode;
|
import org.apache.plc4x.java.api.types.PlcResponseCode;
|
||||||
import org.apache.plc4x.java.api.value.PlcValue;
|
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.DeviceBO;
|
||||||
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
||||||
import org.nl.iot.core.driver.bo.SiteBO;
|
import org.nl.iot.core.driver.bo.SiteBO;
|
||||||
@@ -36,6 +38,7 @@ import java.util.concurrent.TimeUnit;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@ProtocolDriver(ProtocolType.MODBUS)
|
||||||
public class ModBusProtocolDriverImpl implements DriverCustomService {
|
public class ModBusProtocolDriverImpl implements DriverCustomService {
|
||||||
|
|
||||||
private Map<String, PlcConnection> connectMap;
|
private Map<String, PlcConnection> connectMap;
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import lombok.extern.slf4j.Slf4j;
|
|||||||
import org.jinterop.dcom.common.JIException;
|
import org.jinterop.dcom.common.JIException;
|
||||||
import org.jinterop.dcom.core.JIVariant;
|
import org.jinterop.dcom.core.JIVariant;
|
||||||
import org.nl.common.exception.CommonException;
|
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.DeviceBO;
|
||||||
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
||||||
import org.nl.iot.core.driver.bo.SiteBO;
|
import org.nl.iot.core.driver.bo.SiteBO;
|
||||||
@@ -34,6 +36,7 @@ import java.util.concurrent.Executors;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@ProtocolDriver(ProtocolType.OPCDA)
|
||||||
public class OpcDaProtocolDriverImpl implements DriverCustomService {
|
public class OpcDaProtocolDriverImpl implements DriverCustomService {
|
||||||
/**
|
/**
|
||||||
* Opc Da Server Map
|
* 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.enumerated.TimestampsToReturn;
|
||||||
import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
|
import org.eclipse.milo.opcua.stack.core.types.structured.WriteValue;
|
||||||
import org.nl.common.exception.CommonException;
|
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.DeviceBO;
|
||||||
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
||||||
import org.nl.iot.core.driver.bo.SiteBO;
|
import org.nl.iot.core.driver.bo.SiteBO;
|
||||||
@@ -35,6 +37,7 @@ import java.util.concurrent.*;
|
|||||||
|
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@ProtocolDriver(ProtocolType.OPCUA)
|
||||||
public class OpcUaProtocolDriverImpl implements DriverCustomService {
|
public class OpcUaProtocolDriverImpl implements DriverCustomService {
|
||||||
private Map<String, OpcUaClient> connectMap;
|
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.types.PlcResponseCode;
|
||||||
import org.apache.plc4x.java.api.value.PlcValue;
|
import org.apache.plc4x.java.api.value.PlcValue;
|
||||||
import org.nl.common.exception.CommonException;
|
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.DeviceBO;
|
||||||
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
import org.nl.iot.core.driver.bo.MetadataEventDTO;
|
||||||
import org.nl.iot.core.driver.bo.SiteBO;
|
import org.nl.iot.core.driver.bo.SiteBO;
|
||||||
@@ -34,6 +36,7 @@ import java.util.concurrent.*;
|
|||||||
*/
|
*/
|
||||||
@Slf4j
|
@Slf4j
|
||||||
@Service
|
@Service
|
||||||
|
@ProtocolDriver(ProtocolType.PLCS7)
|
||||||
public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ package org.nl;
|
|||||||
import com.alibaba.fastjson.JSONObject;
|
import com.alibaba.fastjson.JSONObject;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
import org.nl.common.exception.CommonException;
|
||||||
|
import org.nl.iot.core.driver.DriverCustomFactory;
|
||||||
|
import org.nl.iot.core.driver.ProtocolType;
|
||||||
import org.nl.iot.core.driver.bo.DeviceBO;
|
import org.nl.iot.core.driver.bo.DeviceBO;
|
||||||
import org.nl.iot.core.driver.bo.SiteBO;
|
import org.nl.iot.core.driver.bo.SiteBO;
|
||||||
import org.nl.iot.core.driver.entity.RValue;
|
import org.nl.iot.core.driver.entity.RValue;
|
||||||
@@ -12,6 +15,7 @@ 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.opcda.OpcDaProtocolDriverImpl;
|
||||||
import org.nl.iot.core.driver.protocol.opcua.OpcUaProtocolDriverImpl;
|
import org.nl.iot.core.driver.protocol.opcua.OpcUaProtocolDriverImpl;
|
||||||
import org.nl.iot.core.driver.protocol.plcs7.PlcS7ProtocolDriverImpl;
|
import org.nl.iot.core.driver.protocol.plcs7.PlcS7ProtocolDriverImpl;
|
||||||
|
import org.nl.iot.core.driver.service.DriverCustomService;
|
||||||
import org.nl.iot.modular.iot.entity.IotConfig;
|
import org.nl.iot.modular.iot.entity.IotConfig;
|
||||||
import org.nl.iot.modular.iot.entity.IotConnect;
|
import org.nl.iot.modular.iot.entity.IotConnect;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
@@ -20,6 +24,7 @@ import org.springframework.test.context.junit4.SpringRunner;
|
|||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -1971,4 +1976,130 @@ public class ApiTest {
|
|||||||
System.err.println("7. 建议先使用OPC客户端工具(如Matrikon OPC Explorer)测试写入");
|
System.err.println("7. 建议先使用OPC客户端工具(如Matrikon OPC Explorer)测试写入");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private DriverCustomFactory driverCustomFactory;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void protocolDriverFactoryTest() {
|
||||||
|
System.out.println("========== 开始测试协议驱动工厂 ==========");
|
||||||
|
|
||||||
|
// 1. 测试获取所有支持的协议类型
|
||||||
|
System.out.println("\n--- 测试1: 获取所有支持的协议类型 ---");
|
||||||
|
Set<ProtocolType> supportedTypes = driverCustomFactory.getSupportedProtocolTypes();
|
||||||
|
System.out.println("支持的协议类型数量: " + supportedTypes.size());
|
||||||
|
for (ProtocolType type : supportedTypes) {
|
||||||
|
System.out.println(" - " + type.name() + ": 主编码=" + type.getMainCode() +
|
||||||
|
", 兼容编码=" + String.join(",", type.getCompatibleCodes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 测试通过主编码获取驱动
|
||||||
|
System.out.println("\n--- 测试2: 通过主编码获取驱动 ---");
|
||||||
|
try {
|
||||||
|
DriverCustomService modbusDriver = driverCustomFactory.getDriver("MODBUS");
|
||||||
|
System.out.println("MODBUS驱动获取成功: " + modbusDriver.getClass().getSimpleName());
|
||||||
|
assert modbusDriver instanceof ModBusProtocolDriverImpl;
|
||||||
|
|
||||||
|
DriverCustomService opcuaDriver = driverCustomFactory.getDriver("OPCUA");
|
||||||
|
System.out.println("OPCUA驱动获取成功: " + opcuaDriver.getClass().getSimpleName());
|
||||||
|
assert opcuaDriver instanceof OpcUaProtocolDriverImpl;
|
||||||
|
|
||||||
|
DriverCustomService plcs7Driver = driverCustomFactory.getDriver("PLCS7");
|
||||||
|
System.out.println("PLCS7驱动获取成功: " + plcs7Driver.getClass().getSimpleName());
|
||||||
|
assert plcs7Driver instanceof PlcS7ProtocolDriverImpl;
|
||||||
|
|
||||||
|
DriverCustomService opcdaDriver = driverCustomFactory.getDriver("OPCDA");
|
||||||
|
System.out.println("OPCDA驱动获取成功: " + opcdaDriver.getClass().getSimpleName());
|
||||||
|
assert opcdaDriver instanceof OpcDaProtocolDriverImpl;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("通过主编码获取驱动失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 测试通过兼容编码获取驱动
|
||||||
|
System.out.println("\n--- 测试3: 通过兼容编码获取驱动 ---");
|
||||||
|
try {
|
||||||
|
DriverCustomService modbusDriver = driverCustomFactory.getDriver("MODBUS-TCP");
|
||||||
|
System.out.println("MODBUS-TCP驱动获取成功: " + modbusDriver.getClass().getSimpleName());
|
||||||
|
assert modbusDriver instanceof ModBusProtocolDriverImpl;
|
||||||
|
|
||||||
|
DriverCustomService opcuaDriver = driverCustomFactory.getDriver("OPC-UA");
|
||||||
|
System.out.println("OPC-UA驱动获取成功: " + opcuaDriver.getClass().getSimpleName());
|
||||||
|
assert opcuaDriver instanceof OpcUaProtocolDriverImpl;
|
||||||
|
|
||||||
|
DriverCustomService plcs7Driver = driverCustomFactory.getDriver("PLC-S7");
|
||||||
|
System.out.println("PLC-S7驱动获取成功: " + plcs7Driver.getClass().getSimpleName());
|
||||||
|
assert plcs7Driver instanceof PlcS7ProtocolDriverImpl;
|
||||||
|
|
||||||
|
DriverCustomService opcdaDriver = driverCustomFactory.getDriver("OPC-DA");
|
||||||
|
System.out.println("OPC-DA驱动获取成功: " + opcdaDriver.getClass().getSimpleName());
|
||||||
|
assert opcdaDriver instanceof OpcDaProtocolDriverImpl;
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("通过兼容编码获取驱动失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 测试大小写不敏感
|
||||||
|
System.out.println("\n--- 测试4: 测试大小写不敏感 ---");
|
||||||
|
try {
|
||||||
|
DriverCustomService driver1 = driverCustomFactory.getDriver("modbus");
|
||||||
|
System.out.println("小写'modbus'获取成功: " + driver1.getClass().getSimpleName());
|
||||||
|
|
||||||
|
DriverCustomService driver2 = driverCustomFactory.getDriver("Modbus-Tcp");
|
||||||
|
System.out.println("混合大小写'Modbus-Tcp'获取成功: " + driver2.getClass().getSimpleName());
|
||||||
|
|
||||||
|
assert driver1 == driver2; // 应该是同一个单例实例
|
||||||
|
System.out.println("验证通过: 返回的是同一个单例实例");
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("大小写测试失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 测试isSupported方法
|
||||||
|
System.out.println("\n--- 测试5: 测试isSupported方法 ---");
|
||||||
|
System.out.println("MODBUS是否支持: " + driverCustomFactory.isSupported("MODBUS"));
|
||||||
|
System.out.println("OPCUA是否支持: " + driverCustomFactory.isSupported("OPCUA"));
|
||||||
|
System.out.println("UNKNOWN是否支持: " + driverCustomFactory.isSupported("UNKNOWN"));
|
||||||
|
System.out.println("modbus-tcp是否支持: " + driverCustomFactory.isSupported("modbus-tcp"));
|
||||||
|
|
||||||
|
// 6. 测试异常情况 - 不支持的协议
|
||||||
|
System.out.println("\n--- 测试6: 测试异常情况 - 不支持的协议 ---");
|
||||||
|
try {
|
||||||
|
driverCustomFactory.getDriver("UNKNOWN_PROTOCOL");
|
||||||
|
System.err.println("错误: 应该抛出异常但没有抛出");
|
||||||
|
} catch (CommonException e) {
|
||||||
|
System.out.println("预期异常捕获成功: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 测试异常情况 - 空值
|
||||||
|
System.out.println("\n--- 测试7: 测试异常情况 - 空值 ---");
|
||||||
|
try {
|
||||||
|
driverCustomFactory.getDriver(null);
|
||||||
|
System.err.println("错误: 应该抛出异常但没有抛出");
|
||||||
|
} catch (CommonException e) {
|
||||||
|
System.out.println("预期异常捕获成功: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
driverCustomFactory.getDriver("");
|
||||||
|
System.err.println("错误: 应该抛出异常但没有抛出");
|
||||||
|
} catch (CommonException e) {
|
||||||
|
System.out.println("预期异常捕获成功: " + e.getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 测试驱动单例性
|
||||||
|
System.out.println("\n--- 测试8: 测试驱动单例性 ---");
|
||||||
|
DriverCustomService driver1 = driverCustomFactory.getDriver("MODBUS");
|
||||||
|
DriverCustomService driver2 = driverCustomFactory.getDriver("MODBUS-TCP");
|
||||||
|
DriverCustomService driver3 = driverCustomFactory.getDriver("modbus");
|
||||||
|
|
||||||
|
System.out.println("driver1 == driver2: " + (driver1 == driver2));
|
||||||
|
System.out.println("driver2 == driver3: " + (driver2 == driver3));
|
||||||
|
System.out.println("driver1 == driver3: " + (driver1 == driver3));
|
||||||
|
assert driver1 == driver2 && driver2 == driver3;
|
||||||
|
System.out.println("验证通过: 所有引用指向同一个单例实例");
|
||||||
|
|
||||||
|
System.out.println("\n========== 协议驱动工厂测试完成 ==========");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,261 +0,0 @@
|
|||||||
package org.nl;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
import org.nl.iot.core.driver.bo.AttributeBO;
|
|
||||||
import org.nl.iot.core.driver.entity.RValue;
|
|
||||||
import org.nl.iot.core.driver.protocol.opcda.OpcDaProtocolDriverImpl;
|
|
||||||
import org.nl.iot.modular.iot.entity.IotConfig;
|
|
||||||
import org.nl.iot.modular.iot.entity.IotConnect;
|
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
|
||||||
import org.springframework.boot.test.context.SpringBootTest;
|
|
||||||
import org.springframework.test.context.junit4.SpringRunner;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* OPC DA 连接测试 - 多种认证方案
|
|
||||||
*
|
|
||||||
* 使用说明:
|
|
||||||
* 1. 确保 Matrikon OPC 模拟器正在运行
|
|
||||||
* 2. 确保已配置 DCOM 权限(参考 OPC_DA_DCOM_完整配置指南.md)
|
|
||||||
* 3. 依次尝试不同的测试方法,直到连接成功
|
|
||||||
*/
|
|
||||||
@RunWith(SpringRunner.class)
|
|
||||||
@SpringBootTest(classes = Application.class)
|
|
||||||
public class OpcDaConnectionTest {
|
|
||||||
|
|
||||||
@Autowired
|
|
||||||
private OpcDaProtocolDriverImpl opcDaProtocolDriver;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方案 1:空用户名和密码(本地连接最简单)
|
|
||||||
* 适用场景:本地测试,DCOM 配置为允许匿名访问
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithEmptyCredentials() {
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("测试方案 1:空用户名和密码");
|
|
||||||
System.out.println("========================================");
|
|
||||||
|
|
||||||
testConnection("", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方案 2:使用当前 Java 进程用户
|
|
||||||
* 适用场景:使用当前登录的 Windows 用户
|
|
||||||
* 注意:需要输入正确的 Windows 密码
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithCurrentUser() {
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("测试方案 2:当前用户");
|
|
||||||
System.out.println("========================================");
|
|
||||||
|
|
||||||
String currentUser = System.getProperty("user.name");
|
|
||||||
System.out.println("当前用户: " + currentUser);
|
|
||||||
System.out.println("请在代码中填入该用户的 Windows 密码!");
|
|
||||||
|
|
||||||
// TODO: 替换为你的 Windows 密码
|
|
||||||
String password = "YOUR_PASSWORD_HERE";
|
|
||||||
|
|
||||||
if ("YOUR_PASSWORD_HERE".equals(password)) {
|
|
||||||
System.err.println("错误:请先在代码中设置正确的密码!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testConnection(currentUser, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方案 3:使用 计算机名\用户名 格式
|
|
||||||
* 适用场景:明确指定本地计算机的用户
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithComputerNameUser() {
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("测试方案 3:计算机名\\用户名");
|
|
||||||
System.out.println("========================================");
|
|
||||||
|
|
||||||
String computerName = System.getenv("COMPUTERNAME");
|
|
||||||
String userName = System.getProperty("user.name");
|
|
||||||
String fullUserName = computerName + "\\" + userName;
|
|
||||||
|
|
||||||
System.out.println("计算机名: " + computerName);
|
|
||||||
System.out.println("用户名: " + userName);
|
|
||||||
System.out.println("完整用户名: " + fullUserName);
|
|
||||||
System.out.println("请在代码中填入该用户的 Windows 密码!");
|
|
||||||
|
|
||||||
// TODO: 替换为你的 Windows 密码
|
|
||||||
String password = "YOUR_PASSWORD_HERE";
|
|
||||||
|
|
||||||
if ("YOUR_PASSWORD_HERE".equals(password)) {
|
|
||||||
System.err.println("错误:请先在代码中设置正确的密码!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testConnection(fullUserName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方案 4:使用 Administrator 账户
|
|
||||||
* 适用场景:使用管理员账户
|
|
||||||
* 注意:确保 Administrator 账户已启用
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithAdministrator() {
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("测试方案 4:Administrator 账户");
|
|
||||||
System.out.println("========================================");
|
|
||||||
|
|
||||||
System.out.println("注意:确保 Administrator 账户已启用");
|
|
||||||
System.out.println("启用命令:net user Administrator /active:yes");
|
|
||||||
System.out.println("请在代码中填入 Administrator 的密码!");
|
|
||||||
|
|
||||||
// TODO: 替换为 Administrator 的密码
|
|
||||||
String password = "YOUR_ADMIN_PASSWORD_HERE";
|
|
||||||
|
|
||||||
if ("YOUR_ADMIN_PASSWORD_HERE".equals(password)) {
|
|
||||||
System.err.println("错误:请先在代码中设置 Administrator 的密码!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testConnection("Administrator", password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 方案 5:使用 .\用户名 格式(本地用户)
|
|
||||||
* 适用场景:明确指定本地用户
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void testWithDotUser() {
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("测试方案 5:.\\用户名 格式");
|
|
||||||
System.out.println("========================================");
|
|
||||||
|
|
||||||
String userName = System.getProperty("user.name");
|
|
||||||
String fullUserName = ".\\" + userName;
|
|
||||||
|
|
||||||
System.out.println("用户名: " + fullUserName);
|
|
||||||
System.out.println("请在代码中填入该用户的 Windows 密码!");
|
|
||||||
|
|
||||||
// TODO: 替换为你的 Windows 密码
|
|
||||||
String password = "YOUR_PASSWORD_HERE";
|
|
||||||
|
|
||||||
if ("YOUR_PASSWORD_HERE".equals(password)) {
|
|
||||||
System.err.println("错误:请先在代码中设置正确的密码!");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
testConnection(fullUserName, password);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 核心测试方法
|
|
||||||
*/
|
|
||||||
private void testConnection(String username, String password) {
|
|
||||||
opcDaProtocolDriver.initial();
|
|
||||||
|
|
||||||
Map<String, AttributeBO> driverConfig = new HashMap<>();
|
|
||||||
driverConfig.put("host", AttributeBO.builder().value("127.0.0.1").build());
|
|
||||||
driverConfig.put("clsId", AttributeBO.builder().value("F8582CF2-88FB-11D0-B850-00C0F0104305").build());
|
|
||||||
driverConfig.put("username", AttributeBO.builder().value(username).build());
|
|
||||||
driverConfig.put("password", AttributeBO.builder().value(password).build());
|
|
||||||
|
|
||||||
Map<String, AttributeBO> pointConfig = new HashMap<>();
|
|
||||||
pointConfig.put("group", AttributeBO.builder().value("Group0").build());
|
|
||||||
pointConfig.put("tag", AttributeBO.builder().value("Random.Int1").build());
|
|
||||||
|
|
||||||
IotConnect connect = IotConnect.builder()
|
|
||||||
.id(1)
|
|
||||||
.code("OPC_DA_TEST")
|
|
||||||
.host("127.0.0.1")
|
|
||||||
.protocol("opc-da")
|
|
||||||
.enabled(true)
|
|
||||||
.description("OPC DA 连接测试")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
IotConfig config = IotConfig.builder()
|
|
||||||
.id(1)
|
|
||||||
.connectId(1)
|
|
||||||
.alias("test_sensor")
|
|
||||||
.aliasName("测试传感器")
|
|
||||||
.registerAddress("Random.Int1")
|
|
||||||
.dataType("int")
|
|
||||||
.readonly(true)
|
|
||||||
.enabled(true)
|
|
||||||
.description("OPC DA 测试点位")
|
|
||||||
.build();
|
|
||||||
|
|
||||||
try {
|
|
||||||
System.out.println("\n--- 连接参数 ---");
|
|
||||||
System.out.println("主机: " + driverConfig.get("host").getValue());
|
|
||||||
System.out.println("CLSID: " + driverConfig.get("clsid").getValue());
|
|
||||||
System.out.println("用户名: " + (username.isEmpty() ? "(空)" : username));
|
|
||||||
System.out.println("密码: " + (password.isEmpty() ? "(空)" : "******"));
|
|
||||||
System.out.println("组名: " + pointConfig.get("group").getValue());
|
|
||||||
System.out.println("标签: " + pointConfig.get("tag").getValue());
|
|
||||||
|
|
||||||
System.out.println("\n正在连接...");
|
|
||||||
RValue result = opcDaProtocolDriver.read(driverConfig, pointConfig, connect, config);
|
|
||||||
|
|
||||||
if (result != null) {
|
|
||||||
System.out.println("\n✅ 连接成功!");
|
|
||||||
System.out.println("读取值: " + result.getValue());
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("成功配置:");
|
|
||||||
System.out.println("用户名: " + username);
|
|
||||||
System.out.println("请在正式代码中使用此配置!");
|
|
||||||
System.out.println("========================================");
|
|
||||||
} else {
|
|
||||||
System.err.println("\n❌ 连接失败:返回结果为 null");
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
|
||||||
System.err.println("\n❌ 连接失败");
|
|
||||||
System.err.println("错误信息: " + e.getMessage());
|
|
||||||
|
|
||||||
// 分析错误并给出建议
|
|
||||||
String errorMsg = e.getMessage();
|
|
||||||
if (errorMsg != null) {
|
|
||||||
if (errorMsg.contains("0x00000005") || errorMsg.contains("Access is denied")) {
|
|
||||||
System.err.println("\n可能原因:");
|
|
||||||
System.err.println("1. 用户名或密码错误");
|
|
||||||
System.err.println("2. DCOM 权限未正确配置");
|
|
||||||
System.err.println("3. 用户未在 'Distributed COM Users' 组中");
|
|
||||||
System.err.println("\n建议:");
|
|
||||||
System.err.println("- 检查密码是否正确");
|
|
||||||
System.err.println("- 运行 dcomcnfg 配置权限");
|
|
||||||
System.err.println("- 参考 OPC_DA_DCOM_完整配置指南.md");
|
|
||||||
} else if (errorMsg.contains("0x00000533")) {
|
|
||||||
System.err.println("\n可能原因:");
|
|
||||||
System.err.println("- 用户账户被禁用");
|
|
||||||
System.err.println("\n建议:");
|
|
||||||
System.err.println("- 运行: net user " + username + " /active:yes");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 显示系统信息
|
|
||||||
*/
|
|
||||||
@Test
|
|
||||||
public void showSystemInfo() {
|
|
||||||
System.out.println("\n========================================");
|
|
||||||
System.out.println("系统信息");
|
|
||||||
System.out.println("========================================");
|
|
||||||
System.out.println("计算机名: " + System.getenv("COMPUTERNAME"));
|
|
||||||
System.out.println("当前用户: " + System.getProperty("user.name"));
|
|
||||||
System.out.println("用户主目录: " + System.getProperty("user.home"));
|
|
||||||
System.out.println("操作系统: " + System.getProperty("os.name"));
|
|
||||||
System.out.println("========================================");
|
|
||||||
System.out.println("\n建议测试顺序:");
|
|
||||||
System.out.println("1. testWithEmptyCredentials() - 最简单");
|
|
||||||
System.out.println("2. testWithCurrentUser() - 使用当前用户");
|
|
||||||
System.out.println("3. testWithComputerNameUser() - 完整格式");
|
|
||||||
System.out.println("4. testWithAdministrator() - 管理员账户");
|
|
||||||
System.out.println("5. testWithDotUser() - 本地用户格式");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user