SpringBoot集成ModbusTCP
1. jar依赖
<dependencies>
<dependency>
<groupId>com.digitalpetri.modbus</groupId>
<artifactId>modbus-master-tcp</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.intelligt.modbus</groupId>
<artifactId>jlibmodbus</artifactId>
<version>1.2.9.7</version>
</dependency>
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.0.3</version>
</dependency>
</dependencies>
<!-- modbus4j的下载地址-->
<repositories>
<repository>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
<id>ias-snapshots</id>
<name>Infinite Automation Snapshot Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-snapshot/</url>
</repository>
<repository>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
<id>ias-releases</id>
<name>Infinite Automation Release Repository</name>
<url>https://maven.mangoautomation.net/repository/ias-release/</url>
</repository>
</repositories>
2. modbusSlave
package com.liming.app.modbusTcp.slave;
public interface ModbusEventListener {
int[] readHoldingRegisterRange(int offset, int quantity);
int[] readInputRegisterRange(int offset, int quantity);
void onWriteToSingleCoil(int address, boolean value);
void onWriteToMultipleCoils(int address, int quantity, boolean[] values);
void onWriteToSingleHoldingRegister(int address, int value);
void onWriteToMultipleHoldingRegisters(int address, int quantity, int[] values);
}
package com.liming.app.modbusTcp.slave;
import com.intelligt.modbus.jlibmodbus.Modbus;
import com.intelligt.modbus.jlibmodbus.data.ModbusHoldingRegisters;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.intelligt.modbus.jlibmodbus.exception.ModbusIOException;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlave;
import com.intelligt.modbus.jlibmodbus.slave.ModbusSlaveFactory;
import com.intelligt.modbus.jlibmodbus.tcp.TcpParameters;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Arrays;
import java.util.List;
@Component
public class ModbusSlaveUtil implements CommandLineRunner {
@Override
public void run(String... args) throws Exception {
Thread.sleep(2000);
new Thread(new Runnable() {
@Override
public void run() {
createSalve();
}
}).start();
}
private static int[] arr = new int[]{0,0};
public static ModbusSlave slave ;
public void createSalve(){
try{
initSlave();
System.out.println("slave start...........");
createDataHolder();
closeSlave();
}catch (Exception e){
}
}
public static void initSlave() throws UnknownHostException, ModbusIOException {
TcpParameters tcpParameters = new TcpParameters();
InetAddress adress = InetAddress.getByName("127.0.0.1");
tcpParameters.setHost(adress);
tcpParameters.setKeepAlive(true);
tcpParameters.setPort(503);
slave = ModbusSlaveFactory.createModbusSlaveTCP(tcpParameters);
Modbus.setLogLevel(Modbus.LogLevel.LEVEL_DEBUG);
}
public void createDataHolder() throws ModbusIOException {
MyOwnDataHolder dh = new MyOwnDataHolder();
dh.addEventListener(new ModbusEventListener() {
@Override
public int[] readHoldingRegisterRange(int offset, int quantity){
System.out.println("readHoldingRegisterRange: 读取信息 offset = " + offset+";quantity"+quantity);
return arr;
}
@Override
public int[] readInputRegisterRange(int offset, int quantity){
System.out.println("readInputRegisterRange: 读取信息 offset = " + offset+";quantity"+quantity);
return arr;
}
@Override
public void onWriteToSingleCoil(int address, boolean value) {
System.out
.print("onWriteToSingleCoil: address " + address + ", value " + value);
}
@Override
public void onWriteToMultipleCoils(int address, int quantity, boolean[] values) {
System.out.print("onWriteToMultipleCoils: address " + address + ", quantity "
+ quantity);
}
@Override
public void onWriteToSingleHoldingRegister(int address, int value) {
System.out.print("onWriteToSingleHoldingRegister: address " + address
+ ", value " + value);
}
@Override
public void onWriteToMultipleHoldingRegisters(int address, int quantity,
int[] values) {
System.out.print("onWriteToMultipleHoldingRegisters: address " + address
+ ", quantity " + quantity);
}
});
slave.setDataHolder(dh);
slave.setReadTimeout(15000);
slave.setServerAddress(1);
slave.listen();
}
public static void closeSlave() throws InterruptedException, ModbusIOException {
if (slave.isListening()) {
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
synchronized (slave) {
slave.notifyAll();
}
}));
synchronized (slave) {
slave.wait();
}
slave.shutdown();
System.out.println("slave shutdown........");
}
}
public void updateHoldingRegisters(int offset, int quantity) throws IllegalDataAddressException, IllegalDataValueException {
List<Float> list = Arrays.asList(10.1f,20.3f,89.5f,77.353f);
ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10000);
for(int i=0;i<list.size();i++){
hr.setFloat32At(i*2, list.get(i));
}
slave.getDataHolder().setHoldingRegisters(hr);
}
public void updateInputRegisters(int offset, int quantity) throws IllegalDataAddressException, IllegalDataValueException, ModbusIOException {
List<Float> list = Arrays.asList(10.1f,20.3f,89.5f,77.353f);
ModbusHoldingRegisters hr = new ModbusHoldingRegisters(10000);
for(int i=0;i<list.size();i++){
hr.setFloat32At(i*2, list.get(i));
}
slave.getDataHolder().setInputRegisters(hr);
}
}
package com.liming.app.modbusTcp.slave;
import com.intelligt.modbus.jlibmodbus.data.DataHolder;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataAddressException;
import com.intelligt.modbus.jlibmodbus.exception.IllegalDataValueException;
import com.liming.app.common.cache.CacheSingleton;
import com.liming.app.modbusTcp.HexUtil;
import com.liming.app.common.config.ApplicationContextGetBeanHelper;
import org.springframework.integration.ip.udp.UnicastSendingMessageHandler;
import org.springframework.integration.support.MessageBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class MyOwnDataHolder extends DataHolder {
final List<ModbusEventListener> modbusEventListenerList = new ArrayList<>();
public MyOwnDataHolder() {
}
public void addEventListener(ModbusEventListener listener) {
modbusEventListenerList.add(listener);
}
public boolean removeEventListener(ModbusEventListener listener) {
return modbusEventListenerList.remove(listener);
}
@Override
public int[] readHoldingRegisterRange(int offset, int quantity) throws IllegalDataAddressException {
return handlerData(offset,quantity);
}
public int[] handlerData(int offset, int quantity){
int[] ints = allData();
int[] ints1 = Arrays.copyOfRange(ints, offset, offset+quantity);
System.out.println("ints1 = " + ints1);
return ints1;
}
public int[] allData(){
int[] result = new int[20000];
int number = 0;
for (int i = 0; i < 20000; i++) {
if (i%4==1){
number = 16384;
}else if (i%4==2){
number = 16968;
}else if (i%4==3){
number = 12740;
}else if (i%4==0){
number = 17455;
}
result[i] = number;
}
return result;
}
@Override
public int[] readInputRegisterRange(int offset, int quantity) throws IllegalDataAddressException{
return new int[]{16422,14417,60293,7864,6,5,4,3,2,1};
}
@Override
public void writeHoldingRegister(int offset, int value) throws IllegalDataAddressException,
IllegalDataValueException {
String message = "hello world";
UnicastSendingMessageHandler messageHandler = (UnicastSendingMessageHandler)ApplicationContextGetBeanHelper.getBean("unicastSendingMessageHandler");
messageHandler.handleMessage(MessageBuilder.withPayload(message).build());
super.writeHoldingRegister(offset, value);
}
@Override
public void writeHoldingRegisterRange(int offset, int[] range) {
int count = 0;
ArrayList<String> hexList = new ArrayList<>();
for (int i : range) {
System.out.println("i = " + i);
hexList.add(HexUtil.integerToHex(i));
}
System.out.println("hexList = " + hexList);
List<Double> doubles = HexUtil.hexStrArrToDoubleArr(hexList);
}
@Override
public void writeCoil(int offset, boolean value) throws IllegalDataAddressException,
IllegalDataValueException {
for (ModbusEventListener l : modbusEventListenerList) {
l.onWriteToSingleCoil(offset, value);
}
super.writeCoil(offset, value);
}
@Override
public void writeCoilRange(int offset, boolean[] range) throws IllegalDataAddressException,
IllegalDataValueException {
for (ModbusEventListener l : modbusEventListenerList) {
l.onWriteToMultipleCoils(offset, range.length, range);
}
super.writeCoilRange(offset, range);
}
}
3. modbusMaster
package com.liming.app.modbusTcp.master;
import com.digitalpetri.modbus.FunctionCode;
import com.digitalpetri.modbus.codec.Modbus;
import com.digitalpetri.modbus.master.ModbusTcpMaster;
import com.digitalpetri.modbus.master.ModbusTcpMasterConfig;
import com.digitalpetri.modbus.requests.*;
import com.digitalpetri.modbus.responses.*;
import com.liming.app.common.cache.CacheSingleton;
import io.netty.buffer.ByteBuf;
import io.netty.util.ReferenceCountUtil;
import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
public class ModbusMasterTCP {
public static ModbusTcpMaster master;
public static void initModbusTcpMaster() {
if (master == null) {
ModbusTcpMasterConfig config = new ModbusTcpMasterConfig.Builder("127.0.0.1").setPort(503).build();
master = new ModbusTcpMaster(config);
}
}
public static void release() {
if (master != null) {
master.disconnect();
}
Modbus.releaseSharedResources();
}
public static void readCoils(int slaveId, int address, int quantity) throws Exception {
CompletableFuture<ReadCoilsResponse> future = master.sendRequest(new ReadCoilsRequest(address, quantity), slaveId);
ReadCoilsResponse response = future.get();
if (response != null) {
System.out.println("\nCoils寄存器(0x01)第"+address+"至"+quantity+"数据如下:");
ByteBuf buf = response.getCoilStatus();
toStringOnByteBuf(buf);
ReferenceCountUtil.release(response);
}
}
public static void readDiscreteInputs(int slaveId, int address, int quantity) throws Exception {
CompletableFuture<ReadDiscreteInputsResponse> future =
master.sendRequest(new ReadDiscreteInputsRequest(address, quantity), slaveId);
ReadDiscreteInputsResponse response = future.get();
if (response != null) {
System.out.println("\nDiscreteInputs寄存器(0x02)第"+address+"至"+quantity+"数据如下:");
ByteBuf buf = response.getInputStatus();
toStringOnByteBuf(buf);
ReferenceCountUtil.release(response);
}
}
public static void readHoldingRegisters(int slaveId, int address, int quantity) throws Exception {
CompletableFuture<ReadHoldingRegistersResponse> future =
master.sendRequest(new ReadHoldingRegistersRequest(address, quantity), slaveId);
ReadHoldingRegistersResponse response = future.get();
if (response != null) {
System.out.println("\nHoldingRegisters寄存器(0x03)第"+address+"至"+quantity+"数据如下:");
ByteBuf buf = response.getRegisters();
toStringOnByteBuf(buf);
ReferenceCountUtil.release(response);
}
}
public static void readInputRegisters(int slaveId, int address, int quantity) throws Exception {
CompletableFuture<ReadInputRegistersResponse> future =
master.sendRequest(new ReadInputRegistersRequest(address, quantity), slaveId);
ReadInputRegistersResponse response = future.get();
if (response != null) {
System.out.println("\nInputRegisters寄存器(0x04)第"+address+"至"+quantity+"数据如下:");
ByteBuf buf = response.getRegisters();
toStringOnByteBuf(buf);
ReferenceCountUtil.release(response);
}
}
private static final String SUCCESS_CODE = "0x000000";
private static final String COON_FAIL_CODE = "0x000001";
private static final String EXEC_FAIL_CODE = "0x000002";
private static final String WRITE_FAIL_CODE = "0x000004";
public static String writeHoldingRegisters(Integer address, Integer value, Integer unitId) {
ModbusResponse modbusResponse;
try {
CompletableFuture<ModbusResponse> future = master.sendRequest(new WriteSingleRegisterRequest(address, value), unitId);
modbusResponse = future.get();
if (modbusResponse == null) {
System.out.println("FCSC-ExternalConnection WriteHoldingRegisters:modbusResponse is null ");
return WRITE_FAIL_CODE;
}
FunctionCode functionCode = modbusResponse.getFunctionCode();
System.out.println("FCSC-ExternalConnection functionCode=" + functionCode + " value=" + value);
if (functionCode == FunctionCode.WriteSingleRegister) {
return SUCCESS_CODE;
} else {
return WRITE_FAIL_CODE;
}
} catch (Exception e) {
e.printStackTrace();
return EXEC_FAIL_CODE;
} finally {
}
}
private static final Integer UNIT_ID = 1;
public static String WriteMultipleRegisters(Integer address, Integer quantity, byte[] values) {
try {
WriteMultipleRegistersRequest request = new WriteMultipleRegistersRequest(address, quantity, values);
CompletableFuture<ModbusResponse> future = master.sendRequest(request, UNIT_ID);
ModbusResponse modbusResponse;
modbusResponse = future.get();
if (modbusResponse == null) {
System.out.println("FCSC-ExternalConnection WriteMultipleRegisters:modbusResponse is null ");
return WRITE_FAIL_CODE;
}
FunctionCode functionCode = modbusResponse.getFunctionCode();
System.out.println("FCSC-ExternalConnection functionCode.getCode()===" + functionCode.getCode() + "=" + functionCode);
if (functionCode == FunctionCode.WriteMultipleRegisters) {
return SUCCESS_CODE;
} else {
return WRITE_FAIL_CODE;
}
} catch (InterruptedException e) {
e.printStackTrace();
return EXEC_FAIL_CODE;
} catch (ExecutionException e) {
e.printStackTrace();
return EXEC_FAIL_CODE;
} finally {
}
}
public static byte[] intArrayToByteArray(int[] src, int offset, int length) {
if (src == null || src.length <= 0 || offset < 0 || length <= 0 || offset + length > src.length) {
return null;
}
byte[] dest = new byte[length];
for (int i = 0; i < length; i++) {
dest[i] = (byte) (src[offset + i] & 0xFF);
}
return dest;
}
public static void toStringOnByteBuf(ByteBuf buf,String threadName) {
boolean whole = false;
String binary = "";
while (buf.isReadable()) {
whole = buf.readerIndex() % 2 == 1 ? true : false;
if (!whole) {
System.out.print("index=" + buf.readerIndex() / 2);
}
binary += Integer.toBinaryString((buf.readByte() & 0xFF) + 0x100).substring(1);
if (whole) {
System.out.println(","+threadName+"binary=" + binary + ",value=" + Integer.parseInt(binary, 2));
binary = "";
}
}
}
public static void toStringOnByteBuf(ByteBuf buf) {
boolean whole = false;
String binary = "";
while (buf.isReadable()) {
whole = buf.readerIndex() % 2 == 1 ? true : false;
if (!whole) {
System.out.print("index=" + buf.readerIndex() / 2);
}
binary += Integer.toBinaryString((buf.readByte() & 0xFF) + 0x100).substring(1);
if (whole) {
System.out.println(",binary=" + binary + ",value=" + Integer.parseInt(binary, 2));
binary = "";
}
}
}
public static void main(String[] args) throws Exception {
}
}