pom.xml
<dependency>
<groupId>com.serotonim</groupId>
<artifactId>modbus4j</artifactId>
</dependency>
配置类
@Slf4j
@Configuration
@Component
@Import(com.serotonin.modbus4j.ModbusFactory.class)
public class ModbusConfig {
@Value("${modbus.host}")
private String host;
@Value("${modbus.port}")
private Integer port;
/*@Autowired
private ModbusFactory modbusFactory;*/
/*@Resource
private BeginPowerExchangeService beginPowerExchangeService;*/
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Bean
public TcpMasterExtend modbusMaster() {
IpParameters ipParameters = new IpParameters();
ipParameters.setHost(host);
ipParameters.setPort(port);
/*// 原生TCP 协议
ModbusMaster master = modbusFactory.createTcpMaster(ipParameters, true);*/
//自定处理
TcpMasterExtend master = new TcpMasterExtend(ipParameters,true,stringRedisTemplate);
//设置超时时间
master.setTimeout(3 * 1000);
//设置重连次数
master.setRetries(6);
//初始化
try {
master.init();
} catch (ModbusInitException e) {
log.error("modbus 初始化异常:{}", e.getMessage());
}
return master;
}
}
自定义 TcpMaster
import cn.hutool.core.util.ReflectUtil;
import com.sany.swap.vo.RedisModule;
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.ip.IpParameters;
import com.serotonin.modbus4j.ip.tcp.TcpMaster;
import com.serotonin.modbus4j.msg.*;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.time.Duration;
import java.time.LocalDateTime;
/**
* @Method
* @Author xuyuhao
* @Version 1.0
* @Description
* @Return
* @Exception
* @Date 2021/4/14
*/
@Getter
@Setter
@Slf4j
public class TcpMasterExtend extends TcpMaster {
//在线标识
private volatile static boolean online = false;
//异常时间
private static LocalDateTime exceptionTime = null;
//redis
private StringRedisTemplate redisTemplate;
/**
* 初始构造方法
*
* @param params
* @param keepAlive
* @param redisTemplate
*/
public TcpMasterExtend(IpParameters params,boolean keepAlive,
StringRedisTemplate redisTemplate) {
super(params, keepAlive);
this.redisTemplate = redisTemplate;
}
/**
* 发送消息
*
* @param request
* @return
*/
public ModbusResponse sendEx(ModbusRequest request) throws ModbusTransportException {
try {
ModbusResponse response = null;
if (request instanceof ReadHoldingRegistersRequest) {
Integer startOffset = (Integer) ReflectUtil.getFieldValue(request, "startOffset");
response = send(request);
log.warn("PLC读取寄存器数据 ------> 位置:{},结果:{}", startOffset, null != response ? ((ReadHoldingRegistersResponse) response).getShortData() : "无");
} else if (request instanceof WriteRegisterRequest || request instanceof WriteRegistersRequest) {
response = send(request);
}
if (!online) {
log.warn("PLC设备上线---------->断线时长:{}分钟", null == exceptionTime ? 0 : Duration.between(exceptionTime, LocalDateTime.now()).toMinutes());
}
redisTemplate.opsForValue().set(RedisModule.PLC_ONLINE_STATUS.get(), "true");
online = true;
return response;
} catch (Exception e) {
//如果是连接异常手动处理一下
if (e.getCause() instanceof ConnectException || e.getCause() instanceof SocketTimeoutException) {
if (online) {
exceptionTime = LocalDateTime.now();
log.warn("PLC设备发生连接异常------->{}", e);
}
destroy();
online = false;
redisTemplate.opsForValue().set(RedisModule.PLC_ONLINE_STATUS.get(), "false");
return null;
}
//否则直接抛出
throw e;
}
}
}
modbus操作类
import com.serotonin.modbus4j.exception.ModbusTransportException;
import com.serotonin.modbus4j.msg.*;
import com.sany.swap.api.plc.service.BeginPowerExchangeService;
import com.sany.swap.service.plc.bean.TcpMasterExtend;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import javax.annotation.Resource;
/**
* <p></p>
*
* @author zhangliang@gjkjjt.com
* @version 1.0.0
* @date 2020/6/26 9:43
* <p>
*/
@Slf4j
@Configuration
public class Modbus4jUtils {
@Autowired
private TcpMasterExtend tcpMaster;
@Resource
private BeginPowerExchangeService beginPowerExchangeService;
/**
* @Title readCoilStatus
* @Description: 读(线圈)开关量数据,相当于功能码:01H-读线圈状态
* @params: [ip, slaveId, offset, numberOfRegister]
* @return: boolean[]
* @throws:
*/
public boolean[] readCoilStatus(int slaveId, int offset, int numberOfRegister) {
boolean[] booleans = null;
try {
ReadCoilsRequest request = new ReadCoilsRequest(slaveId, offset, numberOfRegister);
ReadCoilsResponse response = (ReadCoilsResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("readCoilStatus response: message=" + response.getExceptionMessage());
} else {
booleans = response.getBooleanData();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
beginPowerExchangeService.changePlcOnlineStatus(0);
log.error("modbus 连接异常:{}", e.getMessage());
}
return valueRegroup(numberOfRegister, booleans);
}
/**
* @Title readInputStatus
* @Description: 读取外围设备输入的开关量,相当于功能码:02H-读离散输入状态
* @params: [ip, offset, numberOfRegister]
* @return: boolean[]
* @throws:
*/
public boolean[] readInputStatus(int slaveId, int offset, int numberOfRegister) {
boolean[] booleans = null;
try {
ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId, offset, numberOfRegister);
ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("readInputStatus response: message=" + response.getExceptionMessage());
} else {
booleans = response.getBooleanData();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
beginPowerExchangeService.changePlcOnlineStatus(0);
log.error("modbus连接异常:{}", e.getMessage());
}
return valueRegroup(numberOfRegister, booleans);
}
/**
* @Title readHoldingRegister
* @Description: 读取保持寄存器数据,相当于功能码:03H-读保持寄存器
* @params: [ip, offset, numberOfRegister]
* @return: short[]
* @throws:
*/
public short[] readHoldingRegister(int slaveId, int offset, int numberOfRegister) {
short[] result = null;
try {
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfRegister);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("readHoldingRegister response: message=" + response.getExceptionMessage());
} else {
result = response.getShortData();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
log.error("modbus 连接异常:{}", e.getMessage());
beginPowerExchangeService.changePlcOnlineStatus(0);
}
return result;
}
/**
* @Title readInputRegisters
* @Description: 读取外围设备输入的数据,相当于功能码:04H-读输入寄存器
* @params: [ip, offset, numberOfRegister]
* @return: short[]
* @throws:
*/
public short[] readInputRegisters(int slaveId, int offset, int numberOfRegister) {
short[] result = null;
try {
ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, offset, numberOfRegister);
ReadInputRegistersResponse response = (ReadInputRegistersResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("readInputRegisters response: message=" + response.getExceptionMessage());
} else {
result = response.getShortData();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
log.error("modbus 连接异常:{}", e.getMessage());
beginPowerExchangeService.changePlcOnlineStatus(0);
}
return result;
}
/**
* @Title writeCoil
* @Description: 写单个(线圈)开关量数据,相当于功能码:05H-写单个线圈
* @params: [ip, writeOffset, writeValue]
* @return: boolean
* @throws:
*/
public boolean writeCoil(int slaveId, int writeOffset, boolean writeValue) {
boolean result = false;
try {
WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
WriteCoilResponse response = (WriteCoilResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("writeCoil response: message=" + response.getExceptionMessage());
} else {
result = !response.isException();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
log.error("modbus 连接异常:{}", e.getMessage());
beginPowerExchangeService.changePlcOnlineStatus(0);
}
return result;
}
/**
* @Title writeCoils
* @Description: 写多个开关量数据(线圈),相当于功能码:0FH-写多个线圈
* @params: [ip, startOffset, data]
* @return: boolean
* @throws:
*/
public boolean writeCoils(int slaveId, int startOffset, boolean[] data) {
boolean result = false;
try {
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, data);
WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("writeCoils response: message=" + response.getExceptionMessage());
} else {
result = !response.isException();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
log.error("modbus 连接异常:{}", e.getMessage());
beginPowerExchangeService.changePlcOnlineStatus(0);
}
return result;
}
/**
* @Title writeHoldingRegister
* @Description: 写单个保持寄存器,相当于功能码:06H-写单个保持寄存器
* @params: [slaveId, writeOffset, writeValue]
* @return: boolean true 成功 false 失败
* @throws:
*/
public boolean writeHoldingRegister(int slaveId, int writeOffset, short writeValue) {
boolean result = false;
try {
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("writeHoldingRegister response: message=" + response.getExceptionMessage());
} else {
result = !response.isException();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
log.error("modbus 连接异常:{}", e.getMessage());
beginPowerExchangeService.changePlcOnlineStatus(0);
}
return result;
}
/**
* @Title writeHoldingRegisters
* @Description: 写多个保持寄存器,相当于功能码:10H-写多个保持寄存器
* @params: [ip, slaveId, startOffset, data]
* @return: boolean
* @throws:
*/
public boolean writeHoldingRegisters(int slaveId, int startOffset, short[] data) {
boolean result = false;
try {
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, data);
WriteRegistersResponse response = (WriteRegistersResponse) tcpMaster.sendEx(request);
if (response.isException()) {
log.error("writeHoldingRegisters response: message=" + response.getExceptionMessage());
} else {
result = !response.isException();
}
beginPowerExchangeService.changePlcOnlineStatus(1);
} catch (ModbusTransportException e) {
log.error("modbus 连接异常:{}", e.getMessage());
beginPowerExchangeService.changePlcOnlineStatus(0);
}
return result;
}
/**
* @Title valueRegroup
* @Description: 转换工具,将Boolean转换成0,1
* @params: [numberOfBits, values]
* @return: boolean[]
* @throws:
*/
private boolean[] valueRegroup(int numberOfBits, boolean[] values) {
boolean[] bs = new boolean[numberOfBits];
int temp = 1;
for (boolean b : values) {
bs[temp - 1] = b;
temp++;
if (temp > numberOfBits) {
break;
}
}
return bs;
}
}