整合 modbus 小 demo

配置 maven 依赖

1
2
3
4
5
6
7
8
9
<dependency>
<groupId>com.infiniteautomation</groupId>
<artifactId>modbus4j</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>

ModbusServer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Slf4j
public class ModbusServer {

public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 5, 500, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
threadPoolExecutor.execute(new Handler());
// try {
// boolean flag = threadPoolExecutor.awaitTermination(5, TimeUnit.SECONDS);
// if (flag){
// log.info("线程池关闭成功");
// }
// } catch (InterruptedException e) {
// log.error("线程池关闭异常:{}",e.toString());
// e.printStackTrace();
// }
}

public static TcpSlave initModbusServer() {
TcpSlave tcpSlave = new TcpSlave(502, false);
BasicProcessImage pImg = new BasicProcessImage(1);
//线圈 PLC的输出位,开关量,在Modbus中可读可写
pImg.setCoil(0, false);
//离散量 PLC的输入位,开关量,在Modbus中只读
pImg.setInput(1, false);
//输入寄存器 PLC中只能从模拟量输入端改变的寄存器,在Modbus中只读
pImg.setInputRegister(2, (short) 0);
//保存寄存器 PLC中用于输出模拟量信号的寄存器,在Modbus中可读可写
pImg.setHoldingRegister(3, (short) 0);
// 此为 server 设置的值
pImg.setNumeric(RegisterRange.HOLDING_REGISTER, 4, DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED, 10086);
tcpSlave.addProcessImage(pImg);
return tcpSlave;
}

@Slf4j
static class Handler implements Runnable {

@Override
public void run() {
try {
log.info("modbus tcp server initial start!");
ModbusServer.initModbusServer().start();
} catch (ModbusInitException e) {
e.printStackTrace();
}
}
}
}

ModbusClient

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
@Slf4j
public class ModbusClient {
public static void main(String[] args) throws Exception {
IpParameters params = new IpParameters();
params.setHost("localhost");
params.setPort(502);

ModbusMaster master = new ModbusFactory().createTcpMaster(params, false);
master.init();

log.info("测试连接结果:{}", master.testSlaveNode(1) ? "成功": "失败");

// Define the point locator.
BaseLocator<Number> loc = BaseLocator.holdingRegister(1, 4, DataType.TWO_BYTE_INT_UNSIGNED_SWAPPED);
// Get the point value
log.info("获取slaveId:{},offset:{} 的结果为:{}", 1, 4, master.getValue(loc));

//Set the point value
master.setValue(loc, 10000);
log.info("获取slaveId:{},offset:{} 修改后的结果为:{}", 1, 4, master.getValue(loc));

}
}

ModbusReadUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
@Slf4j
public class ModbusReadUtils {

/**
* 工厂。
*/
static ModbusFactory modbusFactory;

static {
if (modbusFactory == null) {
modbusFactory = new ModbusFactory();
}
}

/**
* 获取master
*
* @return
* @throws ModbusInitException
*/
public static ModbusMaster getMaster(String host, Integer port, Boolean isRtu) throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost(host);
params.setPort(port);
params.setEncapsulated(isRtu);//这个属性确定了协议帧是否是通过tcp封装的RTU结构,采用modbus tcp/ip时,要设为false, 采用modbus rtu over tcp/ip时,要设为true
//
// modbusFactory.createRtuMaster(wapper); //RTU 协议
// modbusFactory.createUdpMaster(params);//UDP 协议
// modbusFactory.createAsciiMaster(wrapper);//ASCII 协议
ModbusMaster master = modbusFactory.createTcpMaster(params, true);// TCP 协议
master.setTimeout(3000);
master.setRetries(3);
master.init();

return master;
}

/**
* 读取[01 Coil Status 0x]类型 开关数据
*
* @param slaveId slaveId
* @param offset 位置
* @return 读取值
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
* @throws ModbusInitException 异常
*/
public static Boolean readCoilStatus(String host, Integer port, int slaveId, int offset)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 01 Coil Status
BaseLocator<Boolean> loc = BaseLocator.coilStatus(slaveId, offset);
Boolean value = getMaster(host, port, false).getValue(loc);
return value;
}

/**
* 读取[02 Input Status 1x]类型 开关数据
*
* @param slaveId
* @param offset
* @return
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static Boolean readInputStatus(String host, Integer port, int slaveId, int offset)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 02 Input Status
BaseLocator<Boolean> loc = BaseLocator.inputStatus(slaveId, offset);
Boolean value = getMaster(host, port, false).getValue(loc);
return value;
}

/**
* 读取[03 Holding Register类型 2x]模拟量数据
*
* @param slaveId slave Id
* @param offset 位置
* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
* @return
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
* @throws ModbusInitException 异常
*/
public static Number readHoldingRegister(String host, Integer port, Boolean isRtu, int slaveId, int offset, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 03 Holding Register类型数据读取
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
ModbusMaster master = getMaster(host, port, isRtu);
Number value = master.getValue(loc);
if (!ObjectUtils.isEmpty(master)) {
master.destroy();
}
return value;
}

/**
* 读取[03 Holding Register类型 2x]模拟量数据
*
* @param master 读取客户端
* @param slaveId slave Id
* @param offset 位置
* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
* @return
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
* @throws ModbusInitException 异常
*/
public static Number readHoldingRegister(ModbusMaster master, int slaveId, int offset, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 03 Holding Register类型数据读取
BaseLocator<Number> loc = BaseLocator.holdingRegister(slaveId, offset, dataType);
Number value = master.getValue(loc);
return value;
}


/**
* 读取[04 Input Registers 3x]类型 模拟量数据
*
* @param slaveId slaveId
* @param offset 位置
* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
* @return 返回结果
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
* @throws ModbusInitException 异常
*/
public static Number readInputRegisters(String host, Integer port, Boolean isRtu, int slaveId, int offset, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 04 Input Registers类型数据读取
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
ModbusMaster master = getMaster(host, port, isRtu);
Number value = master.getValue(loc);
if (!ObjectUtils.isEmpty(master)) {
master.destroy();
}
return value;
}

/**
* 读取[04 Input Registers 3x]类型 模拟量数据
*
* @param master 读取客户端
* @param slaveId slaveId
* @param offset 位置
* @param dataType 数据类型,来自com.serotonin.modbus4j.code.DataType
* @return 返回结果
* @throws ModbusTransportException 异常
* @throws ErrorResponseException 异常
* @throws ModbusInitException 异常
*/
public static Number readInputRegisters(ModbusMaster master, int slaveId, int offset, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 04 Input Registers类型数据读取
BaseLocator<Number> loc = BaseLocator.inputRegister(slaveId, offset, dataType);
Number value = master.getValue(loc);

return value;
}

/**
* 批量读取使用方法
*
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static void batchRead(String host, Integer port) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

BatchRead<Integer> batch = new BatchRead<Integer>();

batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
batch.addLocator(1, BaseLocator.inputStatus(1, 0));

ModbusMaster master = getMaster(host, port, false);

batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
}

/**
* 批量读取使用方法
*
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static void batchRead(ModbusMaster master) throws ModbusTransportException, ErrorResponseException, ModbusInitException {

BatchRead<Integer> batch = new BatchRead<Integer>();

batch.addLocator(0, BaseLocator.holdingRegister(1, 1, DataType.FOUR_BYTE_FLOAT));
batch.addLocator(1, BaseLocator.inputStatus(1, 0));

batch.setContiguousRequests(false);
BatchResults<Integer> results = master.send(batch);
}

/**
* 测试
*
* @param args
*/
public static void main(String[] args) {
try {
ModbusMaster master = getMaster("127.0.0.1", 502, false);
// BigInteger number1 = (BigInteger) readCustomeHoldingRegister(master, 1, 0, 10);
ReadHoldingRegistersRequest request = new ReadHoldingRegistersRequest(1, 0, 5);
//Response: 00 01 00 00 00 0B 01 03 08 FA 5F A9 68 91 01 00 00
//Response: 00 01 00 00 00 0D 01 03 0A FA 5F A9 68 91 01 00 00 6F 00
ReadHoldingRegistersResponse response = (ReadHoldingRegistersResponse) master.send(request);
byte[] data = response.getData();
System.out.println(response);

// Float number = (Float)readHoldingRegister("127.0.0.1", 502, 1, 398, 8);
// System.out.println(number);
// float aFloat = ByteBuffer.wrap(ByteUtils.toBytes(number)).order(ByteOrder.BIG_ENDIAN).getFloat();
// System.out.println(aFloat);
// Double number1 = (Double) readHoldingRegister("127.0.0.1", 502, 1, 494, 14);
// System.out.println(number1);
// Double aFloat1 = ByteBuffer.wrap(ByteUtils.toBytes(number1)).order(ByteOrder.BIG_ENDIAN).getDouble();
// System.out.println(aFloat1);
//
//
//
// Integer number2 = (Integer) readHoldingRegister("127.0.0.1", 502, 1, 594, 5);
// System.out.println(number2);
// Integer aFloat2 = ByteBuffer.wrap(ByteUtils.toBytes(number2)).order(ByteOrder.BIG_ENDIAN).getInt();
// System.out.println(aFloat2);
//
//
//
// Long number3 = (Long) readHoldingRegister("127.0.0.1", 502, 1, 596, 4);
// System.out.println(number3.intValue());
// Integer aFloat3 = ByteBuffer.wrap(ByteUtils.toBytes(number3)).order(ByteOrder.BIG_ENDIAN).getInt();
// ModbusWriteUtils.writeHoldingRegister("127.0.0.1", 502, 1, 598, number3,4);
// System.out.println(aFloat3);
} catch (Exception e) {
e.printStackTrace();
}
}

static BigInteger fromLittleEndian(byte[] littleEndianBytes) {
byte[] bigEndianBytes = toLittleEndian(littleEndianBytes); // 先转回大端字节数组
return new BigInteger(bigEndianBytes);
}

static byte[] toLittleEndian(byte[] bigEndianBytes) {
byte[] littleEndianBytes = new byte[bigEndianBytes.length];
for (int i = 0; i < bigEndianBytes.length; i++) {
littleEndianBytes[i] = bigEndianBytes[bigEndianBytes.length - 1 - i];
}
return littleEndianBytes;
}
}

ModbusWriteUtils

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
@Slf4j
public class ModbusWriteUtils {

/**
* 工厂。
*/
static ModbusFactory modbusFactory;

static {
if (modbusFactory == null) {
modbusFactory = new ModbusFactory();
}
}

/**
* 获取tcpMaster
*
* @return
* @throws ModbusInitException
*/
public static ModbusMaster getMaster(String host, Integer port,Boolean isRtu) throws ModbusInitException {
IpParameters params = new IpParameters();
params.setHost(host);
params.setPort(port);
params.setEncapsulated(isRtu);
ModbusMaster tcpMaster = modbusFactory.createTcpMaster(params, true);
tcpMaster.setTimeout(3000);
tcpMaster.setRetries(3);
tcpMaster.init();

return tcpMaster;
}

/**
* 写 [01 Coil Status(0x)]写一个 function ID = 5
*
* @param slaveId slave的ID
* @param writeOffset 位置
* @param writeValue 值
* @return 是否写入成功
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeCoil(String host, Integer port, int slaveId, int writeOffset, boolean writeValue)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster(host, port,false);
// 创建请求
WriteCoilRequest request = new WriteCoilRequest(slaveId, writeOffset, writeValue);
// 发送请求并获取响应对象
WriteCoilResponse response = (WriteCoilResponse) tcpMaster.send(request);
if (response.isException()) {
return false;
} else {
return true;
}
}

/**
* 写[01 Coil Status(0x)] 写多个 function ID = 15
*
* @param slaveId slaveId
* @param startOffset 开始位置
* @param bdata 写入的数据
* @return 是否写入成功
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeCoils(String host, Integer port, int slaveId, int startOffset, boolean[] bdata)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster(host, port,false);
// 创建请求
WriteCoilsRequest request = new WriteCoilsRequest(slaveId, startOffset, bdata);
// 发送请求并获取响应对象
WriteCoilsResponse response = (WriteCoilsResponse) tcpMaster.send(request);
if (response.isException()) {
return false;
} else {
return true;
}

}

/***
* 写[03 Holding Register(4x)] 写一个 function ID = 6
*
* @param slaveId
* @param writeOffset
* @param writeValue
* @return
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeRegister(String host, Integer port, int slaveId, int writeOffset, short writeValue)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster(host, port,false);
// 创建请求对象
WriteRegisterRequest request = new WriteRegisterRequest(slaveId, writeOffset, writeValue);
WriteRegisterResponse response = (WriteRegisterResponse) tcpMaster.send(request);
if (response.isException()) {
log.error("错误信息 {} {} {}", response.getExceptionMessage(), response.getWriteOffset(), response.getWriteValue());
return false;
} else {
return true;
}

}

/**
* 写入[03 Holding Register(4x)]写多个 function ID=16
*
* @param slaveId modbus的slaveID
* @param startOffset 起始位置偏移量值
* @param sdata 写入的数据
* @return 返回是否写入成功
* @throws ModbusTransportException
* @throws ModbusInitException
*/
public static boolean writeRegisters(String host, Integer port, int slaveId, int startOffset, short[] sdata)
throws ModbusTransportException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster(host, port,false);
log.info("host {} port {} ", host, port);
// 创建请求对象
WriteRegistersRequest request = new WriteRegistersRequest(slaveId, startOffset, sdata);
// 发送请求并获取响应对象
ModbusResponse response = tcpMaster.send(request);
if (response.isException()) {
log.error("发送失败 {}", response.getExceptionMessage());
return false;
} else {
return true;
}
}

/**
* 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
*
* @param slaveId
* @param offset
* @param value 写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
* @param dataType ,com.serotonin.modbus4j.code.DataType
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static void writeHoldingRegister(String host, Integer port, Boolean isRtu,int slaveId, int offset, Number value, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 获取master
ModbusMaster tcpMaster = getMaster(host, port,isRtu);
// 类型
log.info("Modbus 数据写入中ip {} port{} slaveId {} offset {} value {} dataType {}", host, port, slaveId, offset, value, dataType);
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
log.info(JSONUtils.toJSONString(locator));
tcpMaster.setValue(locator, value);
if (!ObjectUtils.isEmpty(tcpMaster)){
tcpMaster.destroy();
}
}

/**
* 写入数字类型的模拟量(如:写入Float类型的模拟量、Double类型模拟量、整数类型Short、Integer、Long)
*
* @param slaveId
* @param offset
* @param value 写入值,Number的子类,例如写入Float浮点类型,Double双精度类型,以及整型short,int,long
* @param dataType ,com.serotonin.modbus4j.code.DataType
* @throws ModbusTransportException
* @throws ErrorResponseException
* @throws ModbusInitException
*/
public static void writeHoldingRegister(ModbusMaster master, int slaveId, int offset, Number value, int dataType)
throws ModbusTransportException, ErrorResponseException, ModbusInitException {
// 类型
log.info("Modbus 数据写入中 slaveId {} offset {} value {} dataType {}", slaveId, offset, value, dataType);
BaseLocator<Number> locator = BaseLocator.holdingRegister(slaveId, offset, dataType);
log.info(JSONUtils.toJSONString(locator));
master.setValue(locator, value);
}

public static void main(String[] args) {
try {
//@formatter:off
// 测试01
// boolean t01 = writeCoil(1, 0, true);
// System.out.println("T01:" + t01);

// 测试02
// boolean t02 = writeCoils(1, 0, new boolean[] { true, false, true });
// System.out.println("T02:" + t02);

// 测试03
// short v = -3;
// boolean t03 = writeRegister(1, 0, v);
// System.out.println("T03:" + t03);
// 测试04
// boolean t04 = writeRegisters(1, 0, new short[] { -3, 3, 9 });
// System.out.println("t04:" + t04);
//写模拟量
for (int i = 0; i < 103; i++) {
int i1 = new Random().nextInt(103 - 0) + 0;
writeHoldingRegister("127.0.0.1", 504,false, 1, 0, i, DataType.TWO_BYTE_INT_SIGNED);
}

//@formatter:on
} catch (Exception e) {
e.printStackTrace();
}

}

}