|
|
@@ -1,6 +1,6 @@
|
|
|
package com.dcs.equipment.utils;
|
|
|
|
|
|
-import com.dcs.equipment.domain.ModbusRequest;
|
|
|
+import com.dcs.equipment.domain.DeviceRequest;
|
|
|
import com.dcs.hnyz.domain.EquipmentParam;
|
|
|
import com.intelligt.modbus.jlibmodbus.Modbus;
|
|
|
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
|
|
|
@@ -15,9 +15,11 @@ import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Component;
|
|
|
|
|
|
import javax.annotation.PostConstruct;
|
|
|
+import javax.annotation.PreDestroy;
|
|
|
import java.net.InetAddress;
|
|
|
import java.net.UnknownHostException;
|
|
|
import java.util.*;
|
|
|
+import java.util.concurrent.atomic.AtomicBoolean;
|
|
|
|
|
|
import static com.dcs.equipment.constants.ModbusConstants.*;
|
|
|
import static com.dcs.equipment.domain.ModbusTcpEnum.DATA_ADDRESS_LENGTH_MAP;
|
|
|
@@ -37,27 +39,40 @@ public class ModbusUtil {
|
|
|
|
|
|
public static void main(String[] args) throws Exception {
|
|
|
TcpParameters tcpParameters = new TcpParameters();
|
|
|
- InetAddress address = InetAddress.getByName("200.200.200.10");
|
|
|
+ InetAddress address = null;
|
|
|
+ try {
|
|
|
+ address = InetAddress.getByName("200.200.200.10");
|
|
|
+ } catch (UnknownHostException e) {
|
|
|
+ // 日志记录 广播异常信息
|
|
|
+ e.printStackTrace();
|
|
|
+ MASTER = null;
|
|
|
+ }
|
|
|
tcpParameters.setHost(address);
|
|
|
tcpParameters.setPort(502);
|
|
|
tcpParameters.setKeepAlive(true);
|
|
|
Modbus.setAutoIncrementTransactionId(true);
|
|
|
ModbusMaster master = ModbusMasterFactory.createModbusMasterTCP(tcpParameters);
|
|
|
MASTER = master;
|
|
|
- master.connect();// 开启连接
|
|
|
+// master.connect();// 开启连接
|
|
|
+ try {
|
|
|
+ MASTER.connect();
|
|
|
+ } catch (ModbusIOException e) {
|
|
|
+ // 日志记录 广播异常信息
|
|
|
+ e.printStackTrace();
|
|
|
+ MASTER = null;
|
|
|
+ }
|
|
|
// 从机模拟 1线圈 2输入 3保持
|
|
|
int i = 1;
|
|
|
float[] holdingRegistersFloatValues = getHoldingRegistersFloatValues(1, 712, 26);
|
|
|
for (float f : holdingRegistersFloatValues) {
|
|
|
System.out.println("第 " + i++ + " Value: " + f);
|
|
|
}
|
|
|
-// setCo
|
|
|
- master.disconnect();
|
|
|
+ MASTER.disconnect();
|
|
|
}
|
|
|
|
|
|
- public static List<ModbusRequest> getModbusRequests(List<EquipmentParam> equipmentParamList) {
|
|
|
+ public static List<DeviceRequest> getDeviceRequests(List<EquipmentParam> equipmentParamList) {
|
|
|
// 保存modbus请求信息
|
|
|
- List<ModbusRequest> modbusRequestList = new ArrayList<>();
|
|
|
+ List<DeviceRequest> modbusRequestList = new ArrayList<>();
|
|
|
// 根据功能码分类的设备信息
|
|
|
Map<String, List<EquipmentParam>> responseMap = new HashMap<>();
|
|
|
// 根据功能码分类的设备地址信息
|
|
|
@@ -109,12 +124,12 @@ public class ModbusUtil {
|
|
|
int readQuantityLength = MathUtil.ceil((lastAddress - firstAddress), DATA_ADDRESS_LENGTH_MAP.get(dataType));
|
|
|
// 根据读取长度分段读取 超过一次可读取的长度的地址将被分为多个请求
|
|
|
for (int i = 1; i <= MathUtil.ceil(readQuantityLength, DATA_TYPE_LENGTH_MAP.get(dataType)); i++){
|
|
|
- ModbusRequest modbusRequest = new ModbusRequest();
|
|
|
+ DeviceRequest modbusRequest = new DeviceRequest();
|
|
|
// TODO 设备从机地址需要从数据库中获取
|
|
|
- modbusRequest.setSlaveId(1);
|
|
|
- modbusRequest.setFunctionCode(registerCode);
|
|
|
+ modbusRequest.setUnitId(1);
|
|
|
+ modbusRequest.setResource(registerCode);
|
|
|
modbusRequest.setDataType(dataType);
|
|
|
- modbusRequest.setStartAddress(addresses.get(0) + (DATA_TYPE_LENGTH_MAP.get(dataType) * (i - 1) * DATA_ADDRESS_LENGTH_MAP.get(dataType)));
|
|
|
+ modbusRequest.setOffsetOrIndex(addresses.get(0) + (DATA_TYPE_LENGTH_MAP.get(dataType) * (i - 1) * DATA_ADDRESS_LENGTH_MAP.get(dataType)));
|
|
|
int quantity;
|
|
|
// 读取长度是否超过一次可读取的长度
|
|
|
if (readQuantityLength < DATA_TYPE_LENGTH_MAP.get(dataType) * i) {
|
|
|
@@ -124,7 +139,7 @@ public class ModbusUtil {
|
|
|
// 超过
|
|
|
quantity = (int) Math.ceil(DATA_TYPE_LENGTH_MAP.get(dataType));
|
|
|
}
|
|
|
- modbusRequest.setQuantity(quantity);
|
|
|
+ modbusRequest.setLengthOrQos(quantity);
|
|
|
modbusRequestList.add(modbusRequest);
|
|
|
}
|
|
|
}
|
|
|
@@ -132,13 +147,23 @@ public class ModbusUtil {
|
|
|
return modbusRequestList;
|
|
|
}
|
|
|
|
|
|
- private static TcpParameters TCP_PARAMETERS;
|
|
|
private static ModbusMaster MASTER;
|
|
|
- private static final Object MASTER_LOCK = new Object();
|
|
|
+
|
|
|
+ private static final int reconnectingCount = 5; // 单次重连 最多尝试次数
|
|
|
|
|
|
@PostConstruct // 启动时spring容器调用该方法初始化
|
|
|
- public void init() throws InterruptedException {
|
|
|
- TCP_PARAMETERS = new TcpParameters();
|
|
|
+ public void init() {
|
|
|
+ initMasterSetting();
|
|
|
+ try {
|
|
|
+ MASTER.connect();
|
|
|
+ logger.info("连接 PLC");
|
|
|
+ } catch (ModbusIOException e) {
|
|
|
+ logger.error("PLC 初始化连接失败");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void initMasterSetting() {
|
|
|
+ TcpParameters TCP_PARAMETERS = new TcpParameters();
|
|
|
InetAddress address = null;
|
|
|
try {
|
|
|
address = InetAddress.getByName(HOST);
|
|
|
@@ -149,30 +174,60 @@ public class ModbusUtil {
|
|
|
TCP_PARAMETERS.setPort(PORT);
|
|
|
MASTER = ModbusMasterFactory.createModbusMasterTCP(TCP_PARAMETERS);
|
|
|
Modbus.setAutoIncrementTransactionId(true);
|
|
|
- boolean flag = true;
|
|
|
- int i = 0;
|
|
|
- while (flag) {
|
|
|
+ }
|
|
|
+
|
|
|
+ @PreDestroy
|
|
|
+ public void destroy() {
|
|
|
+ if (MASTER!= null && MASTER.isConnected()) {
|
|
|
try {
|
|
|
- getMaster();
|
|
|
- flag = false;
|
|
|
+ MASTER.disconnect();
|
|
|
+ logger.info("PLC 断开连接");
|
|
|
} catch (ModbusIOException e) {
|
|
|
- logger.error("与 PLC 连接失败, 一秒后尝试重连...." + i++);
|
|
|
- logger.error("错误码: " + e.getMessage());
|
|
|
- Thread.sleep(1000);
|
|
|
+ logger.error("与 PLC 断开连接失败");
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ private static final AtomicBoolean reconnecting = new AtomicBoolean(false);
|
|
|
+
|
|
|
private static ModbusMaster getMaster() throws ModbusIOException {
|
|
|
- if (!MASTER.isConnected()) { // 连接断开时,重新连接
|
|
|
- synchronized (MASTER_LOCK) { // 加锁 避免多线程并发连接
|
|
|
- if (!MASTER.isConnected()) {
|
|
|
- logger.error("与 PLC 连接断开,尝试重新连接...");
|
|
|
- MASTER.connect();
|
|
|
+ if (!MASTER.isConnected()) {
|
|
|
+ reconnect(); // 启动后台重连,不会重复启动
|
|
|
+ throw new ModbusIOException("PLC 连接断开,正在尝试自动重连...");
|
|
|
+ }
|
|
|
+ return MASTER;
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void reconnect() {
|
|
|
+ if (reconnecting.get()) return; // 已在重连中,直接返回
|
|
|
+
|
|
|
+ if (reconnecting.compareAndSet(false, true)) {
|
|
|
+ new Thread(() -> {
|
|
|
+ try {
|
|
|
+ for (int i = 0; i < reconnectingCount; i++) { // 单次重连 最多尝试次数
|
|
|
+ try {
|
|
|
+ logger.warn("PLC 连接断开,开始重连...");
|
|
|
+ MASTER.connect();
|
|
|
+ if (MASTER.isConnected()) {
|
|
|
+ logger.info("PLC 重连成功!");
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ } catch (ModbusIOException e) {
|
|
|
+ logger.error("PLC 重连失败: {}", e.getMessage());
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ Thread.sleep(1000); // 每次重试间隔 1 秒
|
|
|
+ } catch (InterruptedException e) {
|
|
|
+ Thread.currentThread().interrupt();
|
|
|
+ break;
|
|
|
+ }
|
|
|
}
|
|
|
+ } finally {
|
|
|
+ reconnecting.set(false); // 重连结束,允许后续再次触发
|
|
|
}
|
|
|
- }
|
|
|
- return MASTER;
|
|
|
+ }, "PLC-Reconnect-Thread").start();
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
@@ -355,23 +410,23 @@ public class ModbusUtil {
|
|
|
return getCoilValues(slaveId, offset, 1)[0];
|
|
|
}
|
|
|
|
|
|
- public static List<Object> getValues(ModbusRequest request) throws ModbusProtocolException, ModbusNumberException, ModbusIOException {
|
|
|
- switch (request.getFunctionCode()) {
|
|
|
+ public static List<Object> getValues(DeviceRequest request) throws ModbusProtocolException, ModbusNumberException, ModbusIOException {
|
|
|
+ switch (request.getResource()) {
|
|
|
case COIL_FUNCTION_CODE:
|
|
|
- return convertArrayToList(getCoilValues(request.getSlaveId(), request.getStartAddress(), request.getQuantity()));
|
|
|
+ return convertArrayToList(getCoilValues(request.getUnitId(), request.getOffsetOrIndex(), request.getLengthOrQos()));
|
|
|
case INPUT_FUNCTION_CODE:
|
|
|
switch (request.getDataType()) {
|
|
|
case INT_DATA_TYPE:
|
|
|
- return convertArrayToList(getInputRegisterValues(request.getSlaveId(), request.getStartAddress(), request.getQuantity()));
|
|
|
+ return convertArrayToList(getInputRegisterValues(request.getUnitId(), request.getOffsetOrIndex(), request.getLengthOrQos()));
|
|
|
case FLOAT_DATA_TYPE:
|
|
|
- return convertArrayToList(getInputRegisterFloatValues(request.getSlaveId(), request.getStartAddress(), request.getQuantity()));
|
|
|
+ return convertArrayToList(getInputRegisterFloatValues(request.getUnitId(), request.getOffsetOrIndex(), request.getLengthOrQos()));
|
|
|
}
|
|
|
case HOLDING_FUNCTION_CODE:
|
|
|
switch (request.getDataType()) {
|
|
|
case INT_DATA_TYPE:
|
|
|
- return convertArrayToList(getHoldingRegisterValues(request.getSlaveId(), request.getStartAddress(), request.getQuantity()));
|
|
|
+ return convertArrayToList(getHoldingRegisterValues(request.getUnitId(), request.getOffsetOrIndex(), request.getLengthOrQos()));
|
|
|
case FLOAT_DATA_TYPE:
|
|
|
- return convertArrayToList(getHoldingRegistersFloatValues(request.getSlaveId(), request.getStartAddress(), request.getQuantity()));
|
|
|
+ return convertArrayToList(getHoldingRegistersFloatValues(request.getUnitId(), request.getOffsetOrIndex(), request.getLengthOrQos()));
|
|
|
}
|
|
|
default:
|
|
|
return Collections.emptyList();
|