fix:opcda协议批量读

This commit is contained in:
2026-03-19 13:32:14 +08:00
parent bb533bf8b2
commit 29b5a0fb82
4 changed files with 290 additions and 11 deletions

View File

@@ -0,0 +1,105 @@
# OPC DA 测试说明
## 错误Class not registered [0x80040154]
这个错误表示指定的 CLSID 在目标机器上没有注册。需要获取正确的 OPC DA 服务器 CLSID。
## 如何获取正确的 CLSID
### 方法 1使用 Windows 注册表
在 OPC DA 服务器所在的机器192.168.81.251)上执行:
```cmd
# 查找所有 OPC 相关的 CLSID
reg query "HKEY_CLASSES_ROOT\CLSID" /s /f "OPC"
# 或者查找特定的 OPC Server
reg query "HKEY_CLASSES_ROOT\CLSID" /s /f "Kepware"
```
### 方法 2使用 OPC 客户端工具
推荐使用以下工具之一:
- **Matrikon OPC Explorer**(免费)
- **Kepware OPC Quick Client**
- **OPC Foundation 的 OPC Test Client**
步骤:
1. 在工具中连接到 OPC 服务器
2. 查看服务器属性,可以看到 CLSID
3. 复制 CLSID不要包含花括号
### 方法 3查看 OPC 服务器文档
查看你使用的 OPC DA 服务器的安装文档或配置文件。
## 常见 OPC DA 服务器的 CLSID
| OPC 服务器 | CLSID不含花括号 |
|-----------|-------------------|
| Kepware OPC Server | 7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729 |
| Matrikon OPC Server | F8582CF2-88FB-11D0-B850-00C0F0104305 |
| RSLinx OPC Server | A06B0CF6-4E1F-11D3-8F4D-00104B33C6E8 |
| Siemens OPC Server | 各版本不同,需查看文档 |
**注意**:实际的 CLSID 可能因版本不同而不同,请以实际安装的服务器为准。
## 修改测试代码
`ApiTest.java` 中找到以下代码:
```java
properties.put("clsId", "7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // 修改为实际的 CLSID
```
将 CLSID 替换为你从上述方法获取的实际值。
## 其他可能的问题
### 1. DCOM 配置
确保目标机器的 DCOM 配置正确:
1. 运行 `dcomcnfg`
2. 找到 OPC 服务器
3. 配置安全设置和启动权限
### 2. 防火墙设置
确保防火墙允许 DCOM 通信:
- 端口 135RPC
- 动态端口范围(通常是 49152-65535
### 3. 用户权限
确保使用的用户名和密码具有访问 OPC 服务器的权限。
### 4. OPC Core Components
确保目标机器已安装 OPC Core Components
- 下载地址https://opcfoundation.org/developer-tools/samples-and-tools-classic/core-components/
## 标签地址格式
OPC DA 标签地址格式通常为:
```
Channel1.Device1.Tag1
```
具体格式取决于你的 OPC 服务器配置。可以使用 OPC 客户端工具浏览可用的标签。
## 测试步骤
1. 确认 OPC DA 服务器正在运行
2. 获取正确的 CLSID
3. 修改测试代码中的 CLSID
4. 修改标签地址为实际存在的标签
5. 运行测试
## 调试建议
如果仍然遇到问题,可以:
1. 先使用 OPC 客户端工具(如 Matrikon OPC Explorer测试连接
2. 确认可以成功连接和读取数据后,再运行 Java 测试代码
3. 检查日志中的详细错误信息

View File

@@ -1572,7 +1572,7 @@ public class ApiTest {
IotConnect connect = IotConnect.builder()
.id(3)
.code("OPC_DA_001")
.code("B_CBJ01")
.host("192.168.81.251")
.port(0) // OPC DA不使用端口
.properties(JSONObject.toJSONString(properties))
@@ -1585,10 +1585,10 @@ public class ApiTest {
IotConfig config = IotConfig.builder()
.id(2)
.connectId(3)
.alias("tag2")
.alias("to_spec1")
.aliasName("标签2")
.registerAddress("Channel1.Device1.Tag2") // OPC DA标签地址格式通道.设备.标签
.dataType("INT")
.dataType("STRING")
.readonly(false) // 设置为可写
.enabled(true)
.description("测试OPC DA写入")
@@ -1614,7 +1614,7 @@ public class ApiTest {
// 转换为SiteBO
SiteBO siteBO = SiteBO.builder()
.deviceCode(connect.getCode())
.deviceCode("B_CBJ01.B_CBJ01")
.alias(config.getAlias())
.aliasName(config.getAliasName())
.registerAddress(config.getRegisterAddress())
@@ -1623,7 +1623,7 @@ public class ApiTest {
.build();
// 构建写入值对象
String writeValue = "100"; // 要写入的值
String writeValue = "open|123|第三方"; // 要写入的值
WValue wValue = WValue.builder()
.point(siteBO)
.value(writeValue)
@@ -1637,7 +1637,7 @@ public class ApiTest {
if (result != null && result) {
System.out.println("写入成功!");
System.out.println("写入结果: " + result);
System.out.println("写入结果: " + true);
} else {
System.out.println("写入失败!");
}
@@ -1815,4 +1815,160 @@ public class ApiTest {
System.err.println("6. 建议先使用OPC客户端工具如Matrikon OPC Explorer测试连接");
}
}
@Test
public void opcDaBatchWriteTest() {
// 构建OPC DA连接对象
JSONObject properties = new JSONObject();
properties.put("clsId", "7BC0CC8E-482C-47CA-ABDC-0FE7F9C6E729"); // Kepware OPC Server CLSID
properties.put("username", "administrator");
properties.put("password", "P@ssw0rd.");
IotConnect connect = IotConnect.builder()
.id(4)
.code("B_CBJ01")
.host("192.168.81.251")
.port(135) // OPC DA默认端口
.properties(JSONObject.toJSONString(properties))
.protocol("opc-da")
.enabled(true)
.description("测试OPC DA连接")
.build();
// 构建多个配置对象进行批量写入
IotConfig config1 = IotConfig.builder()
.id(1)
.connectId(4)
.alias("to_qty1")
.aliasName("输出值1")
.registerAddress("Channel1.Device1.Output1") // OPC DA标签地址格式
.dataType("WORD")
.readonly(false) // 设置为可写
.enabled(true)
.description("测试批量写入1")
.build();
IotConfig config2 = IotConfig.builder()
.id(2)
.connectId(4)
.alias("to_spec2")
.aliasName("输出值2")
.registerAddress("Channel1.Device1.Output2")
.dataType("STRING")
.readonly(false) // 设置为可写
.enabled(true)
.description("测试批量写入2")
.build();
IotConfig config3 = IotConfig.builder()
.id(3)
.connectId(4)
.alias("to_spec1")
.aliasName("输出值3")
.registerAddress("Channel1.Device1.Output3")
.dataType("STRING")
.readonly(false) // 设置为可写
.enabled(true)
.description("测试批量写入3")
.build();
// 执行批量写入操作
try {
System.out.println("========== 开始测试OPC DA批量写入 ==========");
System.out.println("连接信息: " + connect.getHost() + ":" + connect.getPort());
System.out.println("CLSID: " + properties.getString("clsId"));
System.out.println("用户名: " + properties.getString("username"));
// 转换为DeviceBO
DeviceBO deviceBO = DeviceBO.builder()
.id(String.valueOf(connect.getId()))
.code(connect.getCode())
.properties(connect.getProperties())
.host(connect.getHost())
.port(connect.getPort())
.protocol(connect.getProtocol())
.build();
// 转换为SiteBO并构建WValue列表
java.util.List<WValue> wValueList = java.util.Arrays.asList(
WValue.builder()
.point(SiteBO.builder()
.deviceCode("B_CBJ01.B_CBJ01")
.alias(config1.getAlias())
.aliasName(config1.getAliasName())
.registerAddress(config1.getRegisterAddress())
.dataType(config1.getDataType())
.readonly(config1.getReadonly())
.build())
.value("12") // 写入值1
.type(config1.getDataType())
.build(),
WValue.builder()
.point(SiteBO.builder()
.deviceCode("B_CBJ01.B_CBJ01")
.alias(config2.getAlias())
.aliasName(config2.getAliasName())
.registerAddress(config2.getRegisterAddress())
.dataType(config2.getDataType())
.readonly(config2.getReadonly())
.build())
.value("open") // 写入值2
.type(config2.getDataType())
.build(),
WValue.builder()
.point(SiteBO.builder()
.deviceCode("B_CBJ01.B_CBJ01")
.alias(config3.getAlias())
.aliasName(config3.getAliasName())
.registerAddress(config3.getRegisterAddress())
.dataType(config3.getDataType())
.readonly(config3.getReadonly())
.build())
.value("dream") // 写入值3
.type(config3.getDataType())
.build()
);
System.out.println("批量写入标签数量: " + wValueList.size());
for (WValue wValue : wValueList) {
SiteBO site = wValue.getPoint();
System.out.println(" - " + site.getAliasName() + " (" + site.getAlias() + "): " +
site.getRegisterAddress() + " [" + site.getDataType() + "] = " + wValue.getValue());
}
// 调用驱动批量写入数据
List<WResponse> result = opcDaProtocolDriver.batchWrite(deviceBO, wValueList);
System.out.println("批量写入完成!");
System.out.println("写入结果:");
// 输出每个标签的写入结果
for (WResponse wResponse : result) {
WValue wValue = wResponse.getWValue();
SiteBO site = wValue.getPoint();
Boolean success = wResponse.getIsOk();
System.out.println(" - " + site.getAliasName() + " (" + site.getAlias() + "): " +
"写入值=" + wValue.getValue() + " 结果=" + (success ? "成功" : "失败"));
if (!success && wResponse.getExceptionMessage() != null && !wResponse.getExceptionMessage().isEmpty()) {
System.out.println(" 错误信息: " + wResponse.getExceptionMessage());
}
}
System.out.println("========== OPC DA批量写入测试完成 ==========");
} catch (Exception e) {
System.err.println("OPC DA批量写入测试失败: " + e.getMessage());
e.printStackTrace();
// 提供故障排除建议
System.err.println("\n========== 故障排除建议 ==========");
System.err.println("1. 检查OPC DA服务器是否正在运行");
System.err.println("2. 验证CLSID是否正确: " + properties.getString("clsId"));
System.err.println("3. 确认标签地址格式是否正确,且标签可写");
System.err.println("4. 检查DCOM配置和防火墙设置");
System.err.println("5. 验证用户权限和密码");
System.err.println("6. 确认标签的数据类型与写入值匹配");
System.err.println("7. 建议先使用OPC客户端工具如Matrikon OPC Explorer测试写入");
}
}
}