fix:opcda协议批量读

This commit is contained in:
2026-03-17 13:34:43 +08:00
parent 25a1ae37e3
commit bb533bf8b2
4 changed files with 311 additions and 96 deletions

View File

@@ -24,5 +24,8 @@ public class WResponse {
*/
private WValue wValue;
/**
* 异常信息
*/
private String exceptionMessage;
}

View File

@@ -3,11 +3,11 @@ package org.nl.iot.core.driver.protocol.opcda;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import jakarta.annotation.PostConstruct;
import lombok.SneakyThrows;
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.bo.AttributeBO;
import org.nl.iot.core.driver.bo.DeviceBO;
import org.nl.iot.core.driver.bo.MetadataEventDTO;
import org.nl.iot.core.driver.bo.SiteBO;
@@ -22,11 +22,10 @@ import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.common.NotCon
import org.nl.iot.core.driver.protocol.opcda.org.openscada.opc.lib.da.*;
import org.nl.iot.core.driver.protocol.opcda.util.JIVariantToStringUtil;
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.stereotype.Service;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -83,13 +82,11 @@ public class OpcDaProtocolDriverImpl implements DriverCustomService {
@Override
public List<RValue> batchRead(DeviceBO device, List<SiteBO> point) {
// todo
return List.of();
return batchReadValue(getConnector(device), device, point);
}
@Override
public List<WResponse> batchWrite(DeviceBO device, List<WValue> wValue) {
// todo
return List.of();
}
@@ -137,6 +134,29 @@ public class OpcDaProtocolDriverImpl implements DriverCustomService {
}
}
@SneakyThrows
private List<RValue> batchReadValue(Server server, DeviceBO deviceBO, List<SiteBO> points) {
List<RValue> res = new ArrayList<>();
Map<String, Item> itemsMap = getItems(server, deviceBO, points);
for (Item item : itemsMap.values()) {
RValue rValue = new RValue();
rValue.setDeviceBO(deviceBO);
SiteBO site = findSite(points, deviceBO, item.getId());
rValue.setSiteBO(ObjectUtil.isNotEmpty(site) ? site : null);
try {
// 组装数据
String value = readItem(item);
rValue.setValue(value);
rValue.setExceptionMessage("");
} catch (JIException e) {
log.error("读取失败: {} -> {}", item.getId(), e.getMessage());
rValue.setExceptionMessage("读取失败!");
}
res.add(rValue);
}
return res;
}
/**
* 获取 OPC DA 服务器中的 Item 对象
* <p>
@@ -155,6 +175,31 @@ public class OpcDaProtocolDriverImpl implements DriverCustomService {
return group.addItem(groupName + "." + siteBO.getDeviceCode() + "." + siteBO.getAlias());
}
@SneakyThrows
public Map<String, Item> getItems(Server server, DeviceBO deviceBO, List<SiteBO> points) {
Group group;
String groupName = deviceBO.getCode();
try {
group = server.findGroup(groupName);
} catch (UnknownGroupException e) {
group = server.addGroup(groupName);
}
String[] arr = points.stream()
.map(site -> groupName + "." + site.getDeviceCode() + "." + site.getAlias())
.toArray(String[]::new);
return group.addItems(arr);
}
public SiteBO findSite(List<SiteBO> points, DeviceBO deviceBO, String key) {
for (SiteBO point : points) {
String itemKey = deviceBO.getCode() + "." + point.getDeviceCode() + "." + point.getAlias();
if (itemKey.equals(key)) {
return point;
}
}
return null;
}
/**
* 读取 OPC DA 位号值
* <p>
@@ -168,34 +213,6 @@ public class OpcDaProtocolDriverImpl implements DriverCustomService {
*/
public String readItem(Item item) throws JIException {
JIVariant jiVariant = item.read(false).getValue();
// return JIVariantToStringUtil.convertFromOpenSCADA(jiVariant);
// switch (jiVariant.getType()) {
// case JIVariant.VT_I2:
// short shortValue = jiVariant.getObjectAsShort();
// return String.valueOf(shortValue);
// case JIVariant.VT_UI2:
// short shortValue2 = jiVariant.getObjectAsShort();
// return String.valueOf(shortValue2 & 0xFFFF);
// case JIVariant.VT_I4:
// int intValue = jiVariant.getObjectAsInt();
// return String.valueOf(intValue);
// case JIVariant.VT_I8:
// long longValue = jiVariant.getObjectAsLong();
// return String.valueOf(longValue);
// case JIVariant.VT_R4:
// float floatValue = jiVariant.getObjectAsFloat();
// return String.valueOf(floatValue);
// case JIVariant.VT_R8:
// double doubleValue = jiVariant.getObjectAsDouble();
// return String.valueOf(doubleValue);
// case JIVariant.VT_BOOL:
// boolean boolValue = jiVariant.getObjectAsBoolean();
// return String.valueOf(boolValue);
// case JIVariant.VT_BSTR:
// return jiVariant.getObjectAsString2();
// default:
// return jiVariant.getObject().toString();
// }
return String.valueOf(JIVariantToStringUtil.getValue(jiVariant));
}

View File

@@ -158,72 +158,100 @@ public class JIVariantToStringUtil {
return convertToString(itemValue);
}
/**
* 获取JIVariant的实际值适配不同数据类型
* @param jiVariant JIVariant对象
* @return 转换后的实际值空值类型返回null
* @throws CommonException 当DCOM解析异常时抛出
*/
public static Object getValue(JIVariant jiVariant) {
if (jiVariant == null) {
return null;
}
try {
Object e = jiVariant.getObject();
if (e instanceof IJIUnsigned) {
return Integer.valueOf(((IJIUnsigned) e).getValue().intValue());
} else if (e instanceof Boolean) {
return jiVariant.getObjectAsBoolean() ? Integer.valueOf(1) : Integer.valueOf(0);
} else if (e instanceof JIString) {
return ((JIString) e).getString();
} else if (!(e instanceof JIArray)) {
if (e instanceof Integer) {
return jiVariant.getObject();
} else if (e instanceof Short) {
return jiVariant.getObject();
} else if (e instanceof Float) {
return jiVariant.getObject();
} else if (e instanceof Double) {
return jiVariant.getObject();
} else {
if (jiVariant.getType() == 0) {
System.err.println("因类型为emtpy 返回 null");
return null;
} else if (jiVariant.getType() == 1) {
System.err.println("因类型为null 返回 null");
return null;
} else {
return jiVariant.getObject();
}
}
} else {
Class clazz = ((JIArray) e).getArrayClass();
int[] r;
int i;
if (JIUnsignedByte.class.isAssignableFrom(clazz)) {
JIUnsignedByte[] arg7 = (JIUnsignedByte[]) ((JIUnsignedByte[]) ((JIArray) e).getArrayInstance());
r = new int[arg7.length];
for (i = 0; i < arg7.length; ++i) {
r[i] = arg7[i].getValue().byteValue();
}
return r;
} else if (!JIUnsignedShort.class.isAssignableFrom(clazz)) {
if (jiVariant.getType() == 0) {
System.err.println("因类型为emtpy 返回 null");
return null;
} else if (jiVariant.getType() == 1) {
System.err.println("因类型为null 返回 null");
return null;
} else {
return ((JIArray) e).getArrayInstance();
}
} else {
JIUnsignedShort[] array = (JIUnsignedShort[]) ((JIUnsignedShort[]) ((JIArray) e)
.getArrayInstance());
r = new int[array.length];
for (i = 0; i < array.length; ++i) {
r[i] = array[i].getValue().intValue();
}
return r;
}
int type = jiVariant.getType();
// 处理空值类型
if (type == JIVariant.VT_EMPTY || type == JIVariant.VT_NULL) {
return null;
}
Object value = jiVariant.getObject();
// 处理无符号整数类型
if (value instanceof IJIUnsigned) {
return ((IJIUnsigned) value).getValue().intValue();
}
// 处理布尔类型 - 转换为整数表示
if (value instanceof Boolean) {
return jiVariant.getObjectAsBoolean() ? 1 : 0;
}
// 处理字符串类型
if (value instanceof JIString) {
return ((JIString) value).getString();
}
// 处理数组类型
if (value instanceof JIArray) {
return handleArrayValue((JIArray) value, type);
}
// 处理基本数据类型
if (isBasicType(value)) {
return value;
}
// 其他类型直接返回原始对象
return value;
} catch (JIException e) {
throw new CommonException(e.getMessage());
throw new CommonException("JIVariant解析异常: " + e.getMessage(), e);
}
}
/**
* 处理数组类型的值
*/
private static Object handleArrayValue(JIArray jiArray, int type) throws JIException {
Class<?> arrayClass = jiArray.getArrayClass();
Object arrayInstance = jiArray.getArrayInstance();
// 处理无符号字节数组
if (JIUnsignedByte.class.isAssignableFrom(arrayClass)) {
JIUnsignedByte[] byteArray = (JIUnsignedByte[]) arrayInstance;
int[] result = new int[byteArray.length];
for (int i = 0; i < byteArray.length; i++) {
result[i] = byteArray[i].getValue().byteValue() & 0xFF; // 确保无符号
}
return result;
}
// 处理无符号短整型数组
if (JIUnsignedShort.class.isAssignableFrom(arrayClass)) {
JIUnsignedShort[] shortArray = (JIUnsignedShort[]) arrayInstance;
int[] result = new int[shortArray.length];
for (int i = 0; i < shortArray.length; i++) {
result[i] = shortArray[i].getValue().intValue() & 0xFFFF; // 确保无符号
}
return result;
}
// 其他数组类型直接返回数组实例
return arrayInstance;
}
/**
* 判断是否为基本数据类型
*/
private static boolean isBasicType(Object value) {
return value instanceof Integer ||
value instanceof Short ||
value instanceof Float ||
value instanceof Double ||
value instanceof Long ||
value instanceof Byte;
}
}