feat:s7写操作
This commit is contained in:
@@ -18,9 +18,9 @@ import org.nl.iot.core.driver.entity.RValue;
|
|||||||
import org.nl.iot.core.driver.entity.WResponse;
|
import org.nl.iot.core.driver.entity.WResponse;
|
||||||
import org.nl.iot.core.driver.entity.WValue;
|
import org.nl.iot.core.driver.entity.WValue;
|
||||||
import org.nl.iot.core.driver.enums.MetadataOperateTypeEnum;
|
import org.nl.iot.core.driver.enums.MetadataOperateTypeEnum;
|
||||||
import org.nl.iot.core.driver.protocol.modbustcp.util.JavaToModBusPlcValueConvertUtil;
|
import org.nl.iot.core.driver.util.JavaToPlcValueConvertUtil;
|
||||||
import org.nl.iot.core.driver.protocol.modbustcp.util.ModBusTcpUtils;
|
import org.nl.iot.core.driver.protocol.modbustcp.util.ModBusTcpUtils;
|
||||||
import org.nl.iot.core.driver.protocol.modbustcp.util.ModbusPlcValueConvertUtil;
|
import org.nl.iot.core.driver.util.PlcValueConvertUtil;
|
||||||
import org.nl.iot.core.driver.service.DriverCustomService;
|
import org.nl.iot.core.driver.service.DriverCustomService;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
@@ -145,7 +145,7 @@ public class ModBusProtocolDriverImpl implements DriverCustomService {
|
|||||||
log.warn("读取到空值,设备编码:{},地址:{}", point.getAlias(), point.getRegisterAddress());
|
log.warn("读取到空值,设备编码:{},地址:{}", point.getAlias(), point.getRegisterAddress());
|
||||||
}
|
}
|
||||||
// 根据类型转换
|
// 根据类型转换
|
||||||
return ModbusPlcValueConvertUtil.convertPlcValueToString(plcValue, point.getDataType());
|
return PlcValueConvertUtil.convertPlcValueToString(plcValue, point.getDataType());
|
||||||
}
|
}
|
||||||
|
|
||||||
@SneakyThrows
|
@SneakyThrows
|
||||||
@@ -170,7 +170,7 @@ public class ModBusProtocolDriverImpl implements DriverCustomService {
|
|||||||
}
|
}
|
||||||
// 5. 取值并转换
|
// 5. 取值并转换
|
||||||
PlcValue plcValue = readResponse.getPlcValue(point.getAlias());
|
PlcValue plcValue = readResponse.getPlcValue(point.getAlias());
|
||||||
list.add(new RValue(deviceBO, point, ModbusPlcValueConvertUtil.convertPlcValueToString(plcValue, point.getDataType()), ""));
|
list.add(new RValue(deviceBO, point, PlcValueConvertUtil.convertPlcValueToString(plcValue, point.getDataType()), ""));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
list.add(new RValue(deviceBO, point, null, String.format(
|
list.add(new RValue(deviceBO, point, null, String.format(
|
||||||
"读取Modbus失败,设备编码:%s,地址:%s,响应码:%s", point.getAlias(), point.getRegisterAddress(), e.getMessage()
|
"读取Modbus失败,设备编码:%s,地址:%s,响应码:%s", point.getAlias(), point.getRegisterAddress(), e.getMessage()
|
||||||
@@ -190,7 +190,7 @@ public class ModBusProtocolDriverImpl implements DriverCustomService {
|
|||||||
// 构建读取请求
|
// 构建读取请求
|
||||||
String tagName = point.getAlias();
|
String tagName = point.getAlias();
|
||||||
// 校验地址格式
|
// 校验地址格式
|
||||||
if (!ModbusPlcValueConvertUtil.containerType(point.getDataType())) {
|
if (!PlcValueConvertUtil.containerType(point.getDataType())) {
|
||||||
log.warn("Modbus数据类型错误:设备编码:{}", tagName);
|
log.warn("Modbus数据类型错误:设备编码:{}", tagName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -225,7 +225,7 @@ public class ModBusProtocolDriverImpl implements DriverCustomService {
|
|||||||
}
|
}
|
||||||
// modbusMaster.connect();
|
// modbusMaster.connect();
|
||||||
// 2. 设置要写入的值
|
// 2. 设置要写入的值
|
||||||
writeRequestBuilder.addTagAddress(tagName, modbusAddress, JavaToModBusPlcValueConvertUtil.convert(wValue.getValue(), type));
|
writeRequestBuilder.addTagAddress(tagName, modbusAddress, JavaToPlcValueConvertUtil.convert(wValue.getValue(), type));
|
||||||
}
|
}
|
||||||
return writeRequestBuilder;
|
return writeRequestBuilder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,38 +1,31 @@
|
|||||||
package org.nl.iot.core.driver.protocol.plcs7;
|
package org.nl.iot.core.driver.protocol.plcs7;
|
||||||
|
|
||||||
import jakarta.annotation.PostConstruct;
|
import jakarta.annotation.PostConstruct;
|
||||||
import lombok.*;
|
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.apache.plc4x.java.DefaultPlcDriverManager;
|
import org.apache.plc4x.java.DefaultPlcDriverManager;
|
||||||
import org.apache.plc4x.java.api.PlcConnection;
|
import org.apache.plc4x.java.api.PlcConnection;
|
||||||
import org.apache.plc4x.java.api.messages.PlcReadRequest;
|
import org.apache.plc4x.java.api.messages.PlcReadRequest;
|
||||||
import org.apache.plc4x.java.api.messages.PlcReadResponse;
|
import org.apache.plc4x.java.api.messages.PlcReadResponse;
|
||||||
|
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.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.bo.AttributeBO;
|
|
||||||
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;
|
||||||
import org.nl.iot.core.driver.entity.RValue;
|
import org.nl.iot.core.driver.entity.RValue;
|
||||||
import org.nl.iot.core.driver.entity.WResponse;
|
import org.nl.iot.core.driver.entity.WResponse;
|
||||||
import org.nl.iot.core.driver.entity.WValue;
|
import org.nl.iot.core.driver.entity.WValue;
|
||||||
import org.nl.iot.core.driver.enums.AttributeTypeFlagEnum;
|
|
||||||
import org.nl.iot.core.driver.enums.MetadataOperateTypeEnum;
|
import org.nl.iot.core.driver.enums.MetadataOperateTypeEnum;
|
||||||
import org.nl.iot.core.driver.protocol.modbustcp.util.ModbusPlcValueConvertUtil;
|
import org.nl.iot.core.driver.util.JavaToPlcValueConvertUtil;
|
||||||
import org.nl.iot.core.driver.protocol.plcs7.com.github.s7.PlcS7PointVariable;
|
import org.nl.iot.core.driver.util.PlcValueConvertUtil;
|
||||||
import org.nl.iot.core.driver.protocol.plcs7.com.github.s7.api.S7Connector;
|
|
||||||
import org.nl.iot.core.driver.protocol.plcs7.com.github.s7.api.S7Serializer;
|
|
||||||
import org.nl.iot.core.driver.protocol.plcs7.util.PlcS7Utils;
|
import org.nl.iot.core.driver.protocol.plcs7.util.PlcS7Utils;
|
||||||
import org.nl.iot.core.driver.service.DriverCustomService;
|
import org.nl.iot.core.driver.service.DriverCustomService;
|
||||||
import org.nl.iot.modular.iot.entity.IotConfig;
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
@@ -73,16 +66,16 @@ public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RValue read(DeviceBO device, SiteBO point) {
|
public RValue read(DeviceBO device, SiteBO point) {
|
||||||
|
try {
|
||||||
log.debug("Plc S7 Read, connect: {}, config: {}", device, point);
|
log.debug("Plc S7 Read, connect: {}, config: {}", device, point);
|
||||||
PlcConnection myS7Connector = getS7Connector(device);
|
PlcConnection myS7Connector = getS7Connector(device);
|
||||||
String tagAddress = "%" + point.getRegisterAddress() + ":" + point.getDataType();
|
String tagAddress = point.getRegisterAddress() + ":" + point.getDataType();
|
||||||
log.info("构建读取请求 - 标签名: {}, 地址: {}", point.getAlias(), tagAddress);
|
log.info("构建读取请求 - 标签名: {}, 地址: {}", point.getAlias(), tagAddress);
|
||||||
|
|
||||||
PlcReadRequest readRequest = doBuildReadRequest(myS7Connector, Collections.singletonList(point)).build();
|
PlcReadRequest readRequest = doBuildReadRequest(myS7Connector, Collections.singletonList(point)).build();
|
||||||
CompletableFuture<? extends PlcReadResponse> readFuture = readRequest.execute();
|
CompletableFuture<? extends PlcReadResponse> readFuture = readRequest.execute();
|
||||||
PlcReadResponse readResponse;
|
PlcReadResponse readResponse = readFuture.get(10, TimeUnit.SECONDS);
|
||||||
try {
|
|
||||||
readResponse = readFuture.get(10, TimeUnit.SECONDS);
|
|
||||||
if (readResponse.getResponseCode(point.getAlias()) != PlcResponseCode.OK) {
|
if (readResponse.getResponseCode(point.getAlias()) != PlcResponseCode.OK) {
|
||||||
return new RValue(device, point, null, String.format(
|
return new RValue(device, point, null, String.format(
|
||||||
"读取 S7 失败,设备编码:%s,地址:%s,响应码:%s"
|
"读取 S7 失败,设备编码:%s,地址:%s,响应码:%s"
|
||||||
@@ -91,40 +84,50 @@ public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
|||||||
, readResponse.getResponseCode(point.getAlias())
|
, readResponse.getResponseCode(point.getAlias())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
|
||||||
return new RValue(device, point, null, e.getMessage());
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info("读取响应 - 可用标签: {}", readResponse.getTagNames());
|
log.info("读取响应 - 可用标签: {}", readResponse.getTagNames());
|
||||||
|
|
||||||
return new RValue(device, point, ModbusPlcValueConvertUtil.convertPlcValueToString(readResponse.getPlcValue(point.getAlias())
|
return new RValue(device, point, PlcValueConvertUtil.convertPlcValueToString(readResponse.getPlcValue(point.getAlias())
|
||||||
, point.getDataType()), null);
|
, point.getDataType()), null);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("读取S7数据异常,设备编码:{},地址:{}", point.getAlias(), point.getRegisterAddress(), e);
|
||||||
|
return new RValue(device, point, null, e.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<RValue> batchRead(DeviceBO device, List<SiteBO> points) {
|
public List<RValue> batchRead(DeviceBO device, List<SiteBO> points) {
|
||||||
|
try {
|
||||||
return batchReadValue(getS7Connector(device), device, points);
|
return batchReadValue(getS7Connector(device), device, points);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量读取S7数据异常,设备ID:{}", device.getId(), e);
|
||||||
|
List<RValue> errorResults = new ArrayList<>();
|
||||||
|
for (SiteBO point : points) {
|
||||||
|
errorResults.add(new RValue(device, point, null, e.getMessage()));
|
||||||
|
}
|
||||||
|
return errorResults;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean write(DeviceBO device, WValue wValue) {
|
public Boolean write(DeviceBO device, WValue wValue) {
|
||||||
return null;
|
return writeValue(getS7Connector(device), device, wValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<WResponse> batchWrite(DeviceBO device, List<WValue> wValue) {
|
public List<WResponse> batchWrite(DeviceBO device, List<WValue> wValue) {
|
||||||
return List.of();
|
return batchWriteValue(getS7Connector(device), wValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取 PLC S7 连接器
|
* 获取 PLC S7 连接器
|
||||||
*/
|
*/
|
||||||
private PlcConnection getS7Connector(DeviceBO deviceBO) {
|
private PlcConnection getS7Connector(DeviceBO deviceBO) {
|
||||||
|
try {
|
||||||
String deviceId = deviceBO.getId();
|
String deviceId = deviceBO.getId();
|
||||||
PlcConnection connection = connectMap.get(deviceId);
|
PlcConnection connection = connectMap.get(deviceId);
|
||||||
|
|
||||||
// 检查连接是否有效(核心修复:判断连接是否存在且未关闭)
|
// 检查连接是否有效(核心修复:判断连接是否存在且未关闭)
|
||||||
try {
|
|
||||||
if (Objects.isNull(connection) || !connection.isConnected()) {
|
if (Objects.isNull(connection) || !connection.isConnected()) {
|
||||||
// 旧连接失效,先关闭再移除
|
// 旧连接失效,先关闭再移除
|
||||||
if (Objects.nonNull(connection)) {
|
if (Objects.nonNull(connection)) {
|
||||||
@@ -141,27 +144,51 @@ public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
|||||||
connection = new DefaultPlcDriverManager().getConnection(connectionUrl);
|
connection = new DefaultPlcDriverManager().getConnection(connectionUrl);
|
||||||
connectMap.put(deviceId, connection);
|
connectMap.put(deviceId, connection);
|
||||||
}
|
}
|
||||||
|
return connection;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("创建S7连接失败,deviceId: {}", deviceId, e);
|
log.error("创建S7连接失败,deviceId: {}", deviceBO.getId(), e);
|
||||||
throw new CommonException("PLC S7 连接失败:" + e.getMessage());
|
throw new CommonException("PLC S7 连接失败:" + e.getMessage());
|
||||||
}
|
}
|
||||||
return connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static PlcReadRequest.Builder doBuildReadRequest(PlcConnection s7Connection, List<SiteBO> points) {
|
public static PlcReadRequest.Builder doBuildReadRequest(PlcConnection s7Connection, List<SiteBO> points) {
|
||||||
|
try {
|
||||||
PlcReadRequest.Builder readBuilder = s7Connection.readRequestBuilder();
|
PlcReadRequest.Builder readBuilder = s7Connection.readRequestBuilder();
|
||||||
for (SiteBO point : points) {
|
for (SiteBO point : points) {
|
||||||
|
try {
|
||||||
String tagAddress = "%" + point.getRegisterAddress() + ":" + point.getDataType();
|
String tagAddress = "%" + point.getRegisterAddress() + ":" + point.getDataType();
|
||||||
// 构建读取请求
|
// 构建读取请求
|
||||||
String tagName = point.getAlias();
|
String tagName = point.getAlias();
|
||||||
// 校验地址格式
|
// 校验地址格式
|
||||||
if (!ModbusPlcValueConvertUtil.containerType(point.getDataType())) {
|
if (!PlcValueConvertUtil.containerType(point.getDataType())) {
|
||||||
log.warn("S7数据类型错误:设备编码:{}", tagName);
|
log.warn("S7数据类型错误:设备编码:{}", tagName);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
readBuilder.addTagAddress(tagName, tagAddress);
|
readBuilder.addTagAddress(tagName, tagAddress);
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("构建读取请求失败,点位:{}", point.getAlias(), e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return readBuilder;
|
return readBuilder;
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("构建读取请求失败", e);
|
||||||
|
throw new CommonException("构建S7读取请求失败:" + e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static PlcWriteRequest.Builder doBuildWriteRequest(PlcConnection modbusMaster, List<WValue> wValues) {
|
||||||
|
PlcWriteRequest.Builder writeRequestBuilder = modbusMaster.writeRequestBuilder();
|
||||||
|
// 1. 解析配置
|
||||||
|
for (WValue wValue : wValues) {
|
||||||
|
SiteBO point = wValue.getPoint();
|
||||||
|
String type = point.getDataType();
|
||||||
|
String tagAddress = "%" + point.getRegisterAddress() + ":" + type;
|
||||||
|
// 2. 构建读取请求
|
||||||
|
String tagName = point.getAlias();
|
||||||
|
// 2. 设置要写入的值
|
||||||
|
writeRequestBuilder.addTagAddress(tagName, tagAddress, JavaToPlcValueConvertUtil.convert(wValue.getValue(), type));
|
||||||
|
}
|
||||||
|
return writeRequestBuilder;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -171,15 +198,16 @@ public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
|||||||
* @param points
|
* @param points
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@SneakyThrows
|
|
||||||
public List<RValue> batchReadValue(PlcConnection s7Connection, DeviceBO deviceBO, List<SiteBO> points) {
|
public List<RValue> batchReadValue(PlcConnection s7Connection, DeviceBO deviceBO, List<SiteBO> points) {
|
||||||
|
List<RValue> list = new ArrayList<>();
|
||||||
|
try {
|
||||||
// 1. 解析配置
|
// 1. 解析配置
|
||||||
PlcReadRequest.Builder readBuilder = doBuildReadRequest(s7Connection, points);
|
PlcReadRequest.Builder readBuilder = doBuildReadRequest(s7Connection, points);
|
||||||
// 3. 执行请求
|
// 3. 执行请求
|
||||||
PlcReadRequest readRequest = readBuilder.build();
|
PlcReadRequest readRequest = readBuilder.build();
|
||||||
CompletableFuture<? extends PlcReadResponse> readFuture = readRequest.execute();
|
CompletableFuture<? extends PlcReadResponse> readFuture = readRequest.execute();
|
||||||
PlcReadResponse readResponse = readFuture.get(10, TimeUnit.SECONDS);
|
PlcReadResponse readResponse = readFuture.get(10, TimeUnit.SECONDS);
|
||||||
List<RValue> list = new ArrayList<>();
|
|
||||||
// 4.组装数据
|
// 4.组装数据
|
||||||
for (SiteBO point : points) {
|
for (SiteBO point : points) {
|
||||||
try {
|
try {
|
||||||
@@ -193,13 +221,68 @@ public class PlcS7ProtocolDriverImpl implements DriverCustomService {
|
|||||||
}
|
}
|
||||||
// 5. 取值并转换
|
// 5. 取值并转换
|
||||||
PlcValue plcValue = readResponse.getPlcValue(point.getAlias());
|
PlcValue plcValue = readResponse.getPlcValue(point.getAlias());
|
||||||
list.add(new RValue(deviceBO, point, ModbusPlcValueConvertUtil.convertPlcValueToString(plcValue, point.getDataType()), ""));
|
list.add(new RValue(deviceBO, point, PlcValueConvertUtil.convertPlcValueToString(plcValue, point.getDataType()), null));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
log.error("处理单个点位数据异常,设备编码:{},地址:{}", point.getAlias(), point.getRegisterAddress(), e);
|
||||||
list.add(new RValue(deviceBO, point, null, String.format(
|
list.add(new RValue(deviceBO, point, null, String.format(
|
||||||
"读取S7失败,设备编码:%s,地址:%s,响应码:%s", point.getAlias(), point.getRegisterAddress(), e.getMessage()
|
"读取S7失败,设备编码:%s,地址:%s,异常信息:%s", point.getAlias(), point.getRegisterAddress(), e.getMessage()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("批量读取S7数据异常", e);
|
||||||
|
// 如果整个批量读取失败,为所有点位返回异常信息
|
||||||
|
for (SiteBO point : points) {
|
||||||
|
list.add(new RValue(deviceBO, point, null, String.format(
|
||||||
|
"批量读取S7失败,设备编码:%s,地址:%s,异常信息:%s", point.getAlias(), point.getRegisterAddress(), e.getMessage()
|
||||||
)));
|
)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean writeValue(PlcConnection modbusMaster, DeviceBO deviceBO, WValue wValue) {
|
||||||
|
// 1. 解析配置
|
||||||
|
PlcWriteRequest.Builder writeRequestBuilder = doBuildWriteRequest(modbusMaster, Collections.singletonList(wValue));
|
||||||
|
PlcWriteRequest writeRequest = writeRequestBuilder.build();
|
||||||
|
// 3. 执行写入(异步+超时控制)
|
||||||
|
CompletableFuture<? extends PlcWriteResponse> writeFuture = writeRequest.execute();
|
||||||
|
PlcWriteResponse coilResponse;
|
||||||
|
try {
|
||||||
|
coilResponse = writeFuture.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
PlcResponseCode responseCode = coilResponse.getResponseCode(wValue.getPoint().getAlias());
|
||||||
|
return responseCode == PlcResponseCode.OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<WResponse> batchWriteValue(PlcConnection s7Connection, List<WValue> wValues) {
|
||||||
|
List<WResponse> res = new ArrayList<>();
|
||||||
|
PlcWriteRequest.Builder writeRequestBuilder = doBuildWriteRequest(s7Connection, wValues);
|
||||||
|
PlcWriteRequest writeRequest = writeRequestBuilder.build();
|
||||||
|
// 执行写入(异步+超时控制)
|
||||||
|
CompletableFuture<? extends PlcWriteResponse> writeFuture = writeRequest.execute();
|
||||||
|
PlcWriteResponse coilResponse;
|
||||||
|
try {
|
||||||
|
coilResponse = writeFuture.get(10, TimeUnit.SECONDS);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
for (WValue wValue : wValues) {
|
||||||
|
try {
|
||||||
|
PlcResponseCode responseCode = coilResponse.getResponseCode(wValue.getPoint().getAlias());
|
||||||
|
res.add(new WResponse(responseCode == PlcResponseCode.OK,
|
||||||
|
wValue, responseCode == PlcResponseCode.OK ? "" : String.format(
|
||||||
|
"写入S7失败,设备编码:%s,地址:%s,响应码:%s", wValue.getPoint().getAlias(),
|
||||||
|
wValue.getPoint().getRegisterAddress(), responseCode
|
||||||
|
)));
|
||||||
|
} catch (Exception e) {
|
||||||
|
res.add(new WResponse(false, wValue, String.format("写入S7失败,设备编码:%s,地址:%s,响应码:%s"
|
||||||
|
, wValue.getPoint().getAlias(), wValue.getPoint().getRegisterAddress(), e.getMessage()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package org.nl.iot.core.driver.protocol.modbustcp.util;
|
package org.nl.iot.core.driver.util;
|
||||||
|
|
||||||
import java.math.BigInteger;
|
import java.math.BigInteger;
|
||||||
import java.time.Duration;
|
import java.time.Duration;
|
||||||
@@ -13,7 +13,7 @@ import java.time.LocalTime;
|
|||||||
* @author: lyd
|
* @author: lyd
|
||||||
* @date: 2026/3/12
|
* @date: 2026/3/12
|
||||||
*/
|
*/
|
||||||
public class JavaToModBusPlcValueConvertUtil {
|
public class JavaToPlcValueConvertUtil {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 核心转换方法:根据Modbus类型字符串,将String值转为对应的Java类型
|
* 核心转换方法:根据Modbus类型字符串,将String值转为对应的Java类型
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
package org.nl.iot.core.driver.util;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* plc类型转换器 - 适配 plc4x 工具
|
||||||
|
* @author: lyd
|
||||||
|
* @date: 2026/3/16
|
||||||
|
*/
|
||||||
|
public class PlcType4xUtils {
|
||||||
|
}
|
||||||
@@ -1,10 +1,8 @@
|
|||||||
package org.nl.iot.core.driver.protocol.modbustcp.util;
|
package org.nl.iot.core.driver.util;
|
||||||
import org.apache.plc4x.java.api.value.PlcValue;
|
import org.apache.plc4x.java.api.value.PlcValue;
|
||||||
import org.apache.plc4x.java.api.types.PlcValueType;
|
import org.apache.plc4x.java.api.types.PlcValueType;
|
||||||
import org.apache.plc4x.java.api.exceptions.PlcIncompatibleDatatypeException;
|
import org.apache.plc4x.java.api.exceptions.PlcIncompatibleDatatypeException;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
|
||||||
import java.math.BigInteger;
|
|
||||||
import java.time.LocalDate;
|
import java.time.LocalDate;
|
||||||
import java.time.LocalDateTime;
|
import java.time.LocalDateTime;
|
||||||
import java.time.LocalTime;
|
import java.time.LocalTime;
|
||||||
@@ -15,7 +13,7 @@ import java.util.Objects;
|
|||||||
* 修正版:Modbus PlcValue 转换工具类
|
* 修正版:Modbus PlcValue 转换工具类
|
||||||
* 适配PLC4X标准API,移除不存在的具体值类型导入,基于PlcValueType枚举和PlcValue接口实现
|
* 适配PLC4X标准API,移除不存在的具体值类型导入,基于PlcValueType枚举和PlcValue接口实现
|
||||||
*/
|
*/
|
||||||
public class ModbusPlcValueConvertUtil {
|
public class PlcValueConvertUtil {
|
||||||
|
|
||||||
// 对齐PlcValueType枚举的Modbus类型映射(覆盖PLC4X定义的所有基础类型)
|
// 对齐PlcValueType枚举的Modbus类型映射(覆盖PLC4X定义的所有基础类型)
|
||||||
public enum ModbusType {
|
public enum ModbusType {
|
||||||
@@ -506,6 +506,7 @@ public class ApiTest {
|
|||||||
.aliasName("DB1整数值")
|
.aliasName("DB1整数值")
|
||||||
// .registerAddress("DB1.DB0.0")
|
// .registerAddress("DB1.DB0.0")
|
||||||
.registerAddress("DB1.DBD0")
|
.registerAddress("DB1.DBD0")
|
||||||
|
// .registerAddress("DB1:1")
|
||||||
.dataType("DINT")
|
.dataType("DINT")
|
||||||
.readonly(true)
|
.readonly(true)
|
||||||
.enabled(true)
|
.enabled(true)
|
||||||
@@ -573,6 +574,7 @@ public class ApiTest {
|
|||||||
System.out.println("地址: " + config2.getRegisterAddress());
|
System.out.println("地址: " + config2.getRegisterAddress());
|
||||||
System.out.println("数据类型: " + config2.getDataType());
|
System.out.println("数据类型: " + config2.getDataType());
|
||||||
System.out.println("读取结果: " + result2.getValue());
|
System.out.println("读取结果: " + result2.getValue());
|
||||||
|
System.out.println("异常信息: " + result2.getExceptionMessage());
|
||||||
|
|
||||||
// 测试读取实数值
|
// 测试读取实数值
|
||||||
// System.out.println("\n--- 测试读取实数值 ---");
|
// System.out.println("\n--- 测试读取实数值 ---");
|
||||||
@@ -598,4 +600,362 @@ public class ApiTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void plcS7BatchReadTest() {
|
||||||
|
// 构建PLC S7连接对象
|
||||||
|
JSONObject properties = new JSONObject();
|
||||||
|
properties.put("remote-rack", "0");
|
||||||
|
properties.put("remote-slot", "0");
|
||||||
|
properties.put("controller-type", "S7_1200");
|
||||||
|
|
||||||
|
IotConnect connect = IotConnect.builder()
|
||||||
|
.id(2)
|
||||||
|
.code("PLC_S7_001")
|
||||||
|
.host("192.168.81.251")
|
||||||
|
.port(102)
|
||||||
|
.properties(JSONObject.toJSONString(properties))
|
||||||
|
.protocol("plc-s7")
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试PLC S7连接")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建多个配置对象进行批量读取
|
||||||
|
IotConfig config1 = IotConfig.builder()
|
||||||
|
.id(1)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_bool")
|
||||||
|
.aliasName("DB1布尔值")
|
||||||
|
.registerAddress("DB1.DB0.0")
|
||||||
|
.dataType("BOOL")
|
||||||
|
.readonly(true)
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试DB1布尔值读取")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
IotConfig config2 = IotConfig.builder()
|
||||||
|
.id(2)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_int")
|
||||||
|
.aliasName("DB1整数值")
|
||||||
|
.registerAddress("DB1:0")
|
||||||
|
.dataType("INT")
|
||||||
|
.readonly(true)
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试DB1整数读取")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
IotConfig config3 = IotConfig.builder()
|
||||||
|
.id(3)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_real")
|
||||||
|
.aliasName("DB1实数值")
|
||||||
|
.registerAddress("DB1:2")
|
||||||
|
.dataType("REAL")
|
||||||
|
.readonly(true)
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试DB1实数读取")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 执行批量读取操作
|
||||||
|
try {
|
||||||
|
System.out.println("========== 开始测试PLC S7批量读取 ==========");
|
||||||
|
System.out.println("连接信息: " + connect.getHost() + ":" + connect.getPort());
|
||||||
|
System.out.println("控制器类型: " + properties.getString("controller-type"));
|
||||||
|
|
||||||
|
// 转换为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列表
|
||||||
|
java.util.List<SiteBO> siteBOList = java.util.Arrays.asList(
|
||||||
|
// SiteBO.builder()
|
||||||
|
// .deviceCode(connect.getCode())
|
||||||
|
// .alias(config1.getAlias())
|
||||||
|
// .aliasName(config1.getAliasName())
|
||||||
|
// .registerAddress(config1.getRegisterAddress())
|
||||||
|
// .dataType(config1.getDataType())
|
||||||
|
// .readonly(config1.getReadonly())
|
||||||
|
// .build(),
|
||||||
|
SiteBO.builder()
|
||||||
|
.deviceCode(connect.getCode())
|
||||||
|
.alias(config2.getAlias())
|
||||||
|
.aliasName(config2.getAliasName())
|
||||||
|
.registerAddress(config2.getRegisterAddress())
|
||||||
|
.dataType(config2.getDataType())
|
||||||
|
.readonly(config2.getReadonly())
|
||||||
|
.build(),
|
||||||
|
SiteBO.builder()
|
||||||
|
.deviceCode(connect.getCode())
|
||||||
|
.alias(config3.getAlias())
|
||||||
|
.aliasName(config3.getAliasName())
|
||||||
|
.registerAddress(config3.getRegisterAddress())
|
||||||
|
.dataType(config3.getDataType())
|
||||||
|
.readonly(config3.getReadonly())
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
System.out.println("批量读取点位数量: " + siteBOList.size());
|
||||||
|
for (SiteBO site : siteBOList) {
|
||||||
|
System.out.println(" - " + site.getAliasName() + " (" + site.getAlias() + "): " +
|
||||||
|
site.getRegisterAddress() + " [" + site.getDataType() + "]");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用驱动批量读取数据
|
||||||
|
List<RValue> result = plcS7ProtocolDriver.batchRead(deviceBO, siteBOList);
|
||||||
|
|
||||||
|
System.out.println("批量读取完成!");
|
||||||
|
System.out.println("读取结果:");
|
||||||
|
|
||||||
|
// 输出每个点位的读取结果
|
||||||
|
for (RValue rValue : result) {
|
||||||
|
SiteBO site = rValue.getSiteBO();
|
||||||
|
String value = rValue.getValue();
|
||||||
|
System.out.println(" - " + site.getAliasName() + " (" + site.getAlias() + "): " +
|
||||||
|
value + (value == null ? " [" + rValue.getExceptionMessage() + "]" : ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("========== PLC S7批量读取测试完成 ==========");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("PLC S7批量读取测试失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void plcS7WriteTest() {
|
||||||
|
// 构建PLC S7连接对象
|
||||||
|
JSONObject properties = new JSONObject();
|
||||||
|
properties.put("remote-rack", "0");
|
||||||
|
properties.put("remote-slot", "0");
|
||||||
|
properties.put("controller-type", "S7_1200");
|
||||||
|
|
||||||
|
IotConnect connect = IotConnect.builder()
|
||||||
|
.id(2)
|
||||||
|
.code("PLC_S7_001")
|
||||||
|
.host("192.168.81.251")
|
||||||
|
.port(102)
|
||||||
|
.properties(JSONObject.toJSONString(properties))
|
||||||
|
.protocol("plc-s7")
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试PLC S7连接")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建配置对象 - 写入操作
|
||||||
|
IotConfig config = IotConfig.builder()
|
||||||
|
.id(1)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_output")
|
||||||
|
.aliasName("DB1输出值")
|
||||||
|
.registerAddress("DB1.DBW10")
|
||||||
|
.dataType("INT")
|
||||||
|
.readonly(false) // 设置为可写
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试PLC S7写入操作")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 执行写入操作
|
||||||
|
try {
|
||||||
|
System.out.println("========== 开始测试PLC S7写入 ==========");
|
||||||
|
System.out.println("连接信息: " + connect.getHost() + ":" + connect.getPort());
|
||||||
|
System.out.println("控制器类型: " + properties.getString("controller-type"));
|
||||||
|
System.out.println("寄存器地址: " + config.getRegisterAddress());
|
||||||
|
System.out.println("数据类型: " + config.getDataType());
|
||||||
|
|
||||||
|
// 转换为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
|
||||||
|
SiteBO siteBO = SiteBO.builder()
|
||||||
|
.deviceCode(connect.getCode())
|
||||||
|
.alias(config.getAlias())
|
||||||
|
.aliasName(config.getAliasName())
|
||||||
|
.registerAddress(config.getRegisterAddress())
|
||||||
|
.dataType(config.getDataType())
|
||||||
|
.readonly(config.getReadonly())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建写入值对象
|
||||||
|
String writeValue = "123"; // 要写入的值
|
||||||
|
WValue wValue = WValue.builder()
|
||||||
|
.point(siteBO)
|
||||||
|
.value(writeValue)
|
||||||
|
.type(config.getDataType())
|
||||||
|
.build();
|
||||||
|
|
||||||
|
System.out.println("写入值: " + writeValue);
|
||||||
|
|
||||||
|
// 调用驱动写入数据
|
||||||
|
Boolean result = plcS7ProtocolDriver.write(deviceBO, wValue);
|
||||||
|
|
||||||
|
if (result != null && result) {
|
||||||
|
System.out.println("写入成功!");
|
||||||
|
System.out.println("写入结果: " + result);
|
||||||
|
} else {
|
||||||
|
System.out.println("写入失败!");
|
||||||
|
}
|
||||||
|
System.out.println("========== PLC S7写入测试完成 ==========");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("PLC S7写入测试失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void plcS7BatchWriteTest() {
|
||||||
|
// 构建PLC S7连接对象
|
||||||
|
JSONObject properties = new JSONObject();
|
||||||
|
properties.put("remote-rack", "0");
|
||||||
|
properties.put("remote-slot", "0");
|
||||||
|
properties.put("controller-type", "S7_1200");
|
||||||
|
|
||||||
|
IotConnect connect = IotConnect.builder()
|
||||||
|
.id(2)
|
||||||
|
.code("PLC_S7_001")
|
||||||
|
.host("192.168.81.251")
|
||||||
|
.port(102)
|
||||||
|
.properties(JSONObject.toJSONString(properties))
|
||||||
|
.protocol("plc-s7")
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试PLC S7连接")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 构建多个配置对象进行批量写入
|
||||||
|
IotConfig config1 = IotConfig.builder()
|
||||||
|
.id(1)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_output1")
|
||||||
|
.aliasName("DB1输出值1")
|
||||||
|
.registerAddress("DB1.DBW10")
|
||||||
|
.dataType("INT")
|
||||||
|
.readonly(false) // 设置为可写
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试批量写入1")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
IotConfig config2 = IotConfig.builder()
|
||||||
|
.id(2)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_output2")
|
||||||
|
.aliasName("DB1输出值2")
|
||||||
|
.registerAddress("DB1.DBW12")
|
||||||
|
.dataType("INT")
|
||||||
|
.readonly(false) // 设置为可写
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试批量写入2")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
IotConfig config3 = IotConfig.builder()
|
||||||
|
.id(3)
|
||||||
|
.connectId(2)
|
||||||
|
.alias("db1_output3")
|
||||||
|
.aliasName("DB1输出值3")
|
||||||
|
.registerAddress("DB1.DBD14")
|
||||||
|
.dataType("REAL")
|
||||||
|
.readonly(false) // 设置为可写
|
||||||
|
.enabled(true)
|
||||||
|
.description("测试批量写入3")
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// 执行批量写入操作
|
||||||
|
try {
|
||||||
|
System.out.println("========== 开始测试PLC S7批量写入 ==========");
|
||||||
|
System.out.println("连接信息: " + connect.getHost() + ":" + connect.getPort());
|
||||||
|
System.out.println("控制器类型: " + properties.getString("controller-type"));
|
||||||
|
|
||||||
|
// 转换为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(connect.getCode())
|
||||||
|
.alias(config1.getAlias())
|
||||||
|
.aliasName(config1.getAliasName())
|
||||||
|
.registerAddress(config1.getRegisterAddress())
|
||||||
|
.dataType(config1.getDataType())
|
||||||
|
.readonly(config1.getReadonly())
|
||||||
|
.build())
|
||||||
|
.value("100") // 写入值1
|
||||||
|
.type(config1.getDataType())
|
||||||
|
.build(),
|
||||||
|
WValue.builder()
|
||||||
|
.point(SiteBO.builder()
|
||||||
|
.deviceCode(connect.getCode())
|
||||||
|
.alias(config2.getAlias())
|
||||||
|
.aliasName(config2.getAliasName())
|
||||||
|
.registerAddress(config2.getRegisterAddress())
|
||||||
|
.dataType(config2.getDataType())
|
||||||
|
.readonly(config2.getReadonly())
|
||||||
|
.build())
|
||||||
|
.value("200") // 写入值2
|
||||||
|
.type(config2.getDataType())
|
||||||
|
.build(),
|
||||||
|
WValue.builder()
|
||||||
|
.point(SiteBO.builder()
|
||||||
|
.deviceCode(connect.getCode())
|
||||||
|
.alias(config3.getAlias())
|
||||||
|
.aliasName(config3.getAliasName())
|
||||||
|
.registerAddress(config3.getRegisterAddress())
|
||||||
|
.dataType(config3.getDataType())
|
||||||
|
.readonly(config3.getReadonly())
|
||||||
|
.build())
|
||||||
|
.value("25.5") // 写入值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 = plcS7ProtocolDriver.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 ? "成功" : "失败"));
|
||||||
|
}
|
||||||
|
|
||||||
|
System.out.println("========== PLC S7批量写入测试完成 ==========");
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
System.err.println("PLC S7批量写入测试失败: " + e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user