O
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

344 lines
13 KiB

package com.xujie.sys.common.utils;
import com.ghgande.j2mod.modbus.facade.ModbusTCPMaster;
import com.ghgande.j2mod.modbus.procimg.Register;
import com.ghgande.j2mod.modbus.procimg.SimpleRegister;
import com.ghgande.j2mod.modbus.util.BitVector;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.*;
import java.util.stream.Collectors;
public class ModbusUtils {
private static final Logger log = LoggerFactory.getLogger(ModbusUtils.class);
/** Modbus TCP 套接字超时(毫秒),用于建连与读写;弱网或从站响应慢时可适当加大 */
private static final int MODBUS_TCP_TIMEOUT_MS = 20000;
/**
*
* @param highRegister 高 16 位所在寄存器的 {@code getValue()}
* @param lowRegister 低 16 位所在寄存器的 {@code getValue()}
*/
public static float holdingRegistersToFloatBigEndian(int highRegister, int lowRegister) {
int bits = (highRegister & 0xFFFF) << 16 | (lowRegister & 0xFFFF);
return Float.intBitsToFloat(bits);
}
/**
* 读取单个保持寄存器值
*
* @param ip 设备IP地址
* @param port 端口号(默认502)
* @param unitId 单元ID(默认1)
* @param ref 寄存器地址(从0开始)
* @return 寄存器值,失败返回null
*/
public static Integer readSingleRegister(String ip, int port, int unitId, int ref) {
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
master.connect();
Register[] registers = master.readMultipleRegisters(unitId, ref, 1);
if (registers != null && registers.length > 0) {
log.info("读取保持寄存器成功 - IP: {}, Port: {}, Ref: {}, Value: {}",
ip, port, ref, registers[0].getValue());
return registers[0].getValue();
}
} catch (Exception e) {
log.error("读取单个寄存器失败 - IP: {}, Port: {}, Ref: {}, Error: {}",
ip, port, ref, e.getMessage());
throw new RuntimeException("请重试,连接寄存器失败:"+e);
} finally {
try {
master.disconnect();
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
return null;
}
/**
* 读取多个保持寄存器值
*
* @param ip 设备IP地址
* @param port 端口号
* @param ref 起始寄存器地址(从0开始)
* @param count 读取数量
* @return 寄存器值列表,失败返回空列表
*/
public static List<Integer> readMultipleRegisters(String ip, int port, int ref, int count) {
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
master.connect();
Register[] registers = master.readMultipleRegisters(ref, count);
if (registers != null) {
return Arrays.stream(registers)
.map(Register::getValue)
.collect(Collectors.toList());
}
} catch (Exception e) {
log.error("读取多个寄存器失败 - IP: {}, Port: {}, Ref: {}, Count: {}, Error: {}",
ip, port, ref, count, e.getMessage());
} finally {
try {
master.disconnect();
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
return new ArrayList<>();
}
/**
* 读取线圈状态
*
* @param ip 设备IP地址
* @param port 端口号
* @param ref 起始线圈地址(从0开始)
* @param count 读取数量
* @return BitVector对象,失败返回null
*/
public static BitVector readCoils(String ip, int port, int ref, int count) {
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
master.connect();
return master.readCoils(ref, count);
} catch (Exception e) {
log.error("读取线圈失败 - IP: {}, Port: {}, Ref: {}, Count: {}, Error: {}",
ip, port, ref, count, e.getMessage());
} finally {
try {
master.disconnect();
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
return null;
}
/**
* 读取单个线圈状态
*
* @param ip 设备IP地址
* @param port 端口号
* @param ref 线圈地址(从0开始)
* @return true表示ON,false表示OFF,失败返回null
*/
public static Boolean readSingleCoil(String ip, int port, int ref) {
BitVector coils = readCoils(ip, port, ref, 1);
if (coils != null) {
return coils.getBit(0);
}
return null;
}
/**
* 批量读取寄存器(分批读取,每批最多100个)
*
* @param ip 设备IP地址
* @param port 端口号
* @param startRef 起始地址
* @param totalCount 总数量
* @return 寄存器值列表
*/
public static List<Integer> readRegistersBatch(String ip, int port, int startRef, int totalCount) {
List<Integer> result = new ArrayList<>();
int batchSize = 100; // 每批最多读取100个
// 分批读取
for (int i = 0; i < totalCount / batchSize; i++) {
int ref = startRef + i * batchSize;
List<Integer> batch = readMultipleRegisters(ip, port, ref, batchSize);
result.addAll(batch);
}
// 读取剩余部分
int remainder = totalCount % batchSize;
if (remainder > 0) {
int ref = startRef + (totalCount / batchSize) * batchSize;
List<Integer> batch = readMultipleRegisters(ip, port, ref, remainder);
result.addAll(batch);
}
// 验证读取数量
if (result.size() != totalCount) {
log.warn("读取寄存器数量不匹配 - 期望: {}, 实际: {}", totalCount, result.size());
}
return result;
}
public static Map<Integer, Integer> readHoldingRegisters(String ip, int port, int slaveId, int offset, int quantity) {
Map<Integer, Integer> resultMap = new HashMap<>();
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
// 开启连接
master.connect();
// 验证连接是否成功建立
if (!master.isConnected()) {
log.error("连接失败 - IP: {}, Port: {}", ip, port);
return resultMap;
}
// 注意:j2mod 的 ref/address 一般从 0 开始;调用方需自行确保 offset 基准正确
Register[] registers = master.readMultipleRegisters(slaveId, offset, quantity);
if (registers == null) {
log.warn("读取保持寄存器返回null - IP: {}, Port: {}, offset: {}, quantity: {}",
ip, port, offset, quantity);
return resultMap;
}
for (int i = 0; i < registers.length; i++) {
resultMap.put(offset + i, registers[i].getValue());
}
} catch (Exception e) {
log.error("读取保持寄存器失败 - IP: {}, Port: {}, offset: {}, quantity: {}, Error: {}",
ip, port, offset, quantity, e.getMessage(), e);
} finally {
try {
if (master.isConnected()) {
master.disconnect();
}
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
return resultMap;
}
/**
* 连续读取多字保持寄存器,按批拆分后合并。
* Modbus FC03 单次请求的字数超过从站允许上限(常见 100~125)时,从站会返回异常码 0x03 Illegal Data Value。
*/
public static Map<Integer, Integer> readHoldingRegistersBatched(String ip, int port, int slaveId, int offset, int quantity) {
final int maxPerRequest = 100;
Map<Integer, Integer> merged = new HashMap<>();
int pos = offset;
int left = quantity;
while (left > 0) {
int chunk = Math.min(maxPerRequest, left);
Map<Integer, Integer> part = readHoldingRegisters(ip, port, slaveId, pos, chunk);
if (part == null || part.isEmpty()) {
log.warn("分批读取保持寄存器中断 - offset={}, 本批 quantity={}, 已合并 {} 字", pos, chunk, merged.size());
break;
}
merged.putAll(part);
pos += chunk;
left -= chunk;
}
if (merged.size() < quantity) {
log.warn("分批读取保持寄存器未凑满 - 期望 {} 字, 实际 {} 字", quantity, merged.size());
}
return merged;
}
public static Map<Integer, Float> readFloatHoldingRegisters(String ip, int port, int slaveId, int offset, int quantity) {
Map<Integer, Float> resultMap = new HashMap<>();
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
// 开启连接
master.connect();
// 验证连接是否成功建立
if (!master.isConnected()) {
log.error("连接失败 - IP: {}, Port: {}", ip, port);
return resultMap;
}
// 注意:j2mod 的 ref/address 一般从 0 开始;调用方需自行确保 offset 基准正确
Register[] registers = master.readMultipleRegisters(slaveId, offset, quantity);
if (registers == null) {
log.warn("读取保持寄存器返回null - IP: {}, Port: {}, offset: {}, quantity: {}",
ip, port, offset, quantity);
return resultMap;
}
for (int i = 0; i < registers.length; i++) {
resultMap.put(offset + i, Float.valueOf(registers[i].getValue()));
}
} catch (Exception e) {
log.error("读取保持寄存器失败 - IP: {}, Port: {}, offset: {}, quantity: {}, Error: {}",
ip, port, offset, quantity, e.getMessage(), e);
} finally {
try {
if (master.isConnected()) {
master.disconnect();
}
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
return resultMap;
}
public static boolean writeSingleHoldingRegister(String ip, int port, int unitId, int ref, int value) {
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
master.connect();
if (!master.isConnected()) {
log.error("连接失败 - IP: {}, Port: {}", ip, port);
return false;
}
master.writeSingleRegister(unitId, ref, new SimpleRegister(value));
return true;
} catch (Exception e) {
log.error("写入保持寄存器失败 - IP: {}, Port: {}, ref: {}, value: {}, Error: {}",
ip, port, ref, value, e.getMessage(), e);
return false;
} finally {
try {
if (master.isConnected()) {
master.disconnect();
}
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
}
public static void resetRegisters(String ip, int port, int unitId, List<Integer> refs) {
if (refs == null || refs.isEmpty()) {
return;
}
ModbusTCPMaster master = new ModbusTCPMaster(ip, port);
try {
master.setTimeout(MODBUS_TCP_TIMEOUT_MS);
master.connect();
if (!master.isConnected()) {
log.error("连接失败 - IP: {}, Port: {}", ip, port);
return;
}
for (Integer ref : refs) {
if (ref != null) {
master.writeSingleRegister(unitId, ref, new SimpleRegister(0));
}
}
} catch (Exception e) {
log.error("批量复位寄存器失败 - IP: {}, Port: {}, refs: {}, Error: {}",
ip, port, refs, e.getMessage(), e);
} finally {
try {
if (master.isConnected()) {
master.disconnect();
}
} catch (Exception e) {
log.error("断开连接失败", e);
}
}
}
}