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

@@ -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<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========== 协议驱动工厂测试完成 ==========");
}
}

View File

@@ -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("测试方案 4Administrator 账户");
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() - 本地用户格式");
}
}