diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/DriverCustomFactory.java b/nl-iot/src/main/java/org/nl/iot/core/driver/DriverCustomFactory.java index b274ef5..630a5d1 100644 --- a/nl-iot/src/main/java/org/nl/iot/core/driver/DriverCustomFactory.java +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/DriverCustomFactory.java @@ -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; /** * 自定义通信协议驱动工厂 * 通过协议编码获取对应的协议驱动单例对象 - * - *

重要说明: - *

- * + * * @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 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 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()); } /** * 根据协议编码获取对应的协议驱动单例对象 - * - *

返回的驱动实例是 Spring 容器中的 Bean 对象(单例): - *

- * - * @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 getSupportedProtocolTypes() { + return new HashSet<>(Arrays.asList(ProtocolType.values())); + } +} \ No newline at end of file diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/ProtocolDriver.java b/nl-iot/src/main/java/org/nl/iot/core/driver/ProtocolDriver.java new file mode 100644 index 0000000..bdac0e7 --- /dev/null +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/ProtocolDriver.java @@ -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(); +} diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/ProtocolType.java b/nl-iot/src/main/java/org/nl/iot/core/driver/ProtocolType.java new file mode 100644 index 0000000..145a3fb --- /dev/null +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/ProtocolType.java @@ -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(",$", ""); + } +} diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/modbustcp/ModBusProtocolDriverImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/modbustcp/ModBusProtocolDriverImpl.java index 61992ac..09c92af 100644 --- a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/modbustcp/ModBusProtocolDriverImpl.java +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/modbustcp/ModBusProtocolDriverImpl.java @@ -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 connectMap; diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/OpcDaProtocolDriverImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/OpcDaProtocolDriverImpl.java index 065620d..7ab2e5c 100644 --- a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/OpcDaProtocolDriverImpl.java +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcda/OpcDaProtocolDriverImpl.java @@ -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 diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java index 7b33435..aefd046 100644 --- a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/opcua/OpcUaProtocolDriverImpl.java @@ -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 connectMap; diff --git a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java index 631e659..45b500f 100644 --- a/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java +++ b/nl-iot/src/main/java/org/nl/iot/core/driver/protocol/plcs7/PlcS7ProtocolDriverImpl.java @@ -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 { /** diff --git a/nl-web-app/src/test/java/org/nl/ApiTest.java b/nl-web-app/src/test/java/org/nl/ApiTest.java index 67b326a..482b41c 100644 --- a/nl-web-app/src/test/java/org/nl/ApiTest.java +++ b/nl-web-app/src/test/java/org/nl/ApiTest.java @@ -3,6 +3,9 @@ package org.nl; import com.alibaba.fastjson.JSONObject; import org.junit.Test; 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.SiteBO; 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.opcua.OpcUaProtocolDriverImpl; 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.IotConnect; 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.Map; +import java.util.Set; /** * @@ -1971,4 +1976,130 @@ public class ApiTest { 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 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========== 协议驱动工厂测试完成 =========="); + } } diff --git a/nl-web-app/src/test/java/org/nl/OpcDaConnectionTest.java b/nl-web-app/src/test/java/org/nl/OpcDaConnectionTest.java deleted file mode 100644 index a714024..0000000 --- a/nl-web-app/src/test/java/org/nl/OpcDaConnectionTest.java +++ /dev/null @@ -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 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 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() - 本地用户格式"); - } -}