modbus连接PLC
-
创建springboot项目
-
导入pom文件
<dependency> <groupId>com.infiniteautomation</groupId> <artifactId>modbus4j</artifactId> <version>${ modbus.version}</version> </dependency>
3.在com.ncty.modbus.config下创建ModbusConfig类
package com.ncty.modbus.config; /** * @author zyw * @version 1.0 * @date 2021-09-23 11:25 */ import com.serotonin.modbus4j.ModbusFactory; import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.ip.IpParameters; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import java.util.HashMap; @Configuration /* * 使用@Bean,就不用使用@Import导入相应的类,@Bean生成的bean默认的名字是方法名,因为hashMap广泛使用, * 所以使用@Bean引入依赖的方式,以便在注入时指定名称,以免注入错误的对象 * @Import({java.util.HashMap.class,com.serotonin.modbus4j.ModbusFactory.class}) */ @Import(ModbusFactory.class) public class ModbusConfig { @Bean public HashMap<String, ModbusMaster> modbusMasterHashMap() { return new HashMap<>(); } @Autowired private ModbusFactory modbusFactory; @Autowired @Qualifier("modbusMasterHashMap") private HashMap<String,ModbusMaster> masterMap; /** * @Description: 通过ip获取对应的modbus
连接器 * @Param: [ip] * @return: com.serotonin.modbus4j.ModbusMaster * @throws: */ public ModbusMaster getMaster(String ip) { ModbusMaster modbusMaster = masterMap.get(ip); if(modbusMaster == null) { setMaster(ip, 502); modbusMaster = masterMap.get(ip); } return modbusMaster; } /** * @Description: 设置ip对应的modbus连接器 * @Param: [ip, port] * @return: void * @throws: */ private void setMaster(String ip, Integer port) { ModbusMaster master; IpParameters params = new IpParameters(); params.setHost(ip); params.setPort(port); /** * 设置为true,会导致TimeoutException: request=com.serotonin.modbus4j.ip.encap.EncapMessageRequest@774dfba5", * params.setEncapsulated(true); * TCP 协议 */ master = modbusFactory.createTcpMaster(params, false); try { //设置超时间 master.setTimeout(3*1000); //设置重连次数 master.setRetries(3); //初始化 master.init(); } catch (ModbusInitException e) { e.printStackTrace(); } masterMap.put(ip, master); } }
4.在com.ncty.modbus.utils下创建ModbusUtil类
package com.ncty.modbus.utils; import com.ncty.modbus.config.ModbusConfig; import com.serotonin.modbus4j.ModbusMaster; import com.serotonin.modbus4j.exception.ModbusInitException; import com.serotonin.modbus4j.exception.ModbusTransportException; import com.serotonin.modbus4j.msg.*; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; /** * @author zyw * @version 1.0 * @date 2021-08-20 9:58 */ @Component public class ModbusUtil { //从机默认值 private Integer slaveId = 1; @Autowired private ModbusConfig modbusConig;
public boolean[] readCoilStatus(String ip, int offset, int numberOfRegister) throws ModbusTransportException {
ModbusMaster master = modbusConfig.getMaster(ip);
ReadCoilsRequest request = new ReadCoilsRequest(slaveId, offset, numberOfRegister);
ReadCoilsResponse response = (ReadCoilsResponse) master.send(request);
boolean[] booleans = response.getBooleanData();
return valueRegroup(numberOfRegister, booleans);
}
/**
* @Description: 读取外围设备输入的开关量,相当于功能码:02H-读离散输入状态
* @Param: [ip, offset, numberOfRegister]
* @return: boolean[]
* @throws:
*/
public boolean[] readInputStatus(String ip, int offset, int numberOfRegister) throws ModbusTransportException {
ModbusMaster master = modbusConfig.getMaster(ip);
ReadDiscreteInputsRequest request = new ReadDiscreteInputsRequest(slaveId,offset, numberOfRegister);
ReadDiscreteInputsResponse response = (ReadDiscreteInputsResponse) master.send(request);
boolean[] booleans = response.getBooleanData();
return valueRegroup(numberOfRegister, booleans);
}
/**
* @Description: 读取保持寄存器数据,相当于功能码:03H-读保持寄存器
* @Param: [ip, offset, numberOfRegister]
* @return: short[]
* @throws:
*/
public short[] readHoldingRegister(String ip, int offset, int numberOfRegister) throws ModbusTransportException {
ModbusMaster master = modbusConfig.getMaster(ip);
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(slaveId, offset, numberOfRegister);
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
return response.getShortData();
}
/**
* @Description: 读取外围设备输入的数据,相当于功能码:04H-读输入寄存器
* @Param: [ip, offset, numberOfRegister]
* @return: short[]
* @throws:
*/
public short[] readInputRegisters(String ip, int offset, int numberOfRegister) throws ModbusTransportException {
ModbusMaster master = modbusConfig.getMaster(ip);
ReadInputRegistersRequest request = new ReadInputRegistersRequest(slaveId, offset, numberOfRegister);
ReadInputRegistersResponse response = (ReadInputRegistersResponse) master.send(request);
return response.getShortData();
}
/**
* @Description: 写单个(线圈)开关量数据,相当于功能码:05H-写单个线圈
* @Param: [ip, writeOffset, writeValue]
* @return: boolean
* @throws:
* @Author: Ricardo.Liu
* @Date: 2020/5/8
*/
public boolean writeCoil(String ip, int writeOffset, boolean writeValue) throws ModbusTransportException {
ModbusMaster tcpMaster = modbusConfig.getMaster(ip);
WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
return !response.isException();
}
/**
* @Description: 写多个开关量数据(线圈),相当于功能码:0FH-写多个线圈
* @Param: [ip, startOffset, data]
* @return: boolean
* @throws:
* @Author: Ricardo.Liu
* @Date: 2020/5/8
*/
public boolean writeCoils(String ip, int startOffset, boolean[] data) throws ModbusTransportException {
ModbusMaster tcpMaster = modbusConfig.getMaster(ip);
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, data);
WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
return !response.isException();
}
/**
* @Description: 写单个保持寄存器,相当于功能码:06H-写单个保持寄存器
* @Param: [ip, writeOffset, writeValue]
* @return: boolean
* @throws:
*/
public boolean writeHoldingRegister(String ip, int writeOffset, short writeValue) throws ModbusTransportException, ModbusInitException {
ModbusMaster tcpMaster = modbusConfig.getMaster(ip);
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
return !response.isException();
}
/**
* @Description: 写多个保持寄存器,相当于功能码:10H-写多个保持寄存器
* @Param: [ip, startOffset, data]
* @return: boolean
* @throws:
*/
public boolean writeHoldingRegisters(String ip, int startOffset, short[] data) throws ModbusTransportException, ModbusInitException {
ModbusMaster tcpMaster = modbusConfig.getMaster(ip);
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, data);
WriteRegistersResponse response = (WriteRegistersResponse) tcpMaster.send(request);
return !response.isException();
}
/**
* @Description: 转换工具,将Boolean转换成0,1
* @Param: [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;
}
}
5.在com.ncty.modbus.utils下创建ShortAndIntUtils类
package com.ncty.modbus.utils;
import org.springframework.stereotype.Component;
/**
* @author zyw
* @version 1.0
* @date 2021-09-23 16:13
* 转换读写PLC的DWORD转换字节码
*/
@Component
public class ShortAndIntUtils {
public short[] intToShortArray(Integer num){
String str = Integer.toBinaryString(num);
int high = 0;
int low = 0;//后16位
if(str.length() > 16){
high = Integer.parseInt(str.substring(0,str.length()-16) , 2);//前16位
low = Integer.parseInt(str.substring(str.length() -16) , 2);//后16位
}else {
low = Integer.parseInt(str , 2);//后16位
}
if(high > Short.MAX_VALUE){
high = (short) (Short.MAX_VALUE - high);
}
if(low > Short.MAX_VALUE){
low = (short) (Short.MAX_VALUE - low);
}
short[] shorts = {(short) high, (short) low};
return shorts;
}
public int shoToIntValue(short[] num){
String str = "";
for (int item : num) {
if(item < 0){
item = Short.MAX_VALUE-item;
}
str += Integer.toBinaryString(item);
}
return scale2Decimal(str , 2);
}
/**
* 其他进制转十进制
* @param number
* @return
*/
public static int scale2Decimal(String number, int scale) {
String regexp = "^\\d+$";
if (null == number || !number.matches(regexp)) {
throw new IllegalArgumentException("input is not a number");
}
if (2 > scale || scale > 32) {
throw new IllegalArgumentException("scale is not in range");
}
// 不同其他进制转十进制,修改这里即可
int total = 0;
String[] ch = number.split("");
int chLength = ch.length;
for (int i = 0; i < chLength; i++) {
total += Integer.valueOf(ch[i]) * Math.pow(scale, chLength - 1 - i);
}
return total;
}
}
6.在com.ncty.modbus.controller下创建ModbusController类
package com.ncty.modbus.controller;
import com.ncty.modbus.utils.ModbusUtil;
import com.ncty.modbus.utils.ShortAndIntUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author zyw
* @version 1.0
* @date 2021-08-20 9:28
* 测试读写接口
* 下列是读取DWORD类型数据
*/
@RestController
@RequestMapping("/modbus")
public class ModbusController {
@Autowired
private ModbusUtil modbusUtil;
@Autowired
private ShortAndIntUtils shortAndIntUtils;
@RequestMapping("read")
public Integer read() throws Exception {
short[] shorts = modbusUtil.readHoldingRegister("127.0.0.1", 3000, 2);
return shortAndIntUtils.shoToIntValue(shorts);
}
@RequestMapping("write")
public String write() throws Exception {
short[] shorts = shortAndIntUtils.intToShortArray(65535);
modbusUtil.writeHoldingRegisters("localhost",3000, shorts);
return "写入成功";
}
}