1/****************************************************************************
2**
3** Copyright (C) 2017 The Qt Company Ltd.
4** Contact: http://www.qt.io/licensing/
5**
6** This file is part of the QtSerialBus module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL3$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see http://www.qt.io/terms-conditions. For further
15** information use the contact form at http://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPLv3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or later as published by the Free
28** Software Foundation and appearing in the file LICENSE.GPL included in
29** the packaging of this file. Please review the following information to
30** ensure the GNU General Public License version 2.0 requirements will be
31** met: http://www.gnu.org/licenses/gpl-2.0.html.
32**
33** $QT_END_LICENSE$
34**
35****************************************************************************/
36
37#include <QtSerialBus/qmodbusserver.h>
38#if QT_CONFIG(modbus_serialport)
39#include <QtSerialBus/qmodbusrtuserialslave.h>
40#endif
41#include <QtSerialBus/qmodbustcpserver.h>
42#include <QtSerialBus/qmodbusdeviceidentification.h>
43
44#include <QtCore/qdebug.h>
45#include <QtTest/QtTest>
46
47class TestServer : public QModbusServer
48{
49public:
50 TestServer() {
51 qRegisterMetaType<QModbusDataUnit::RegisterType>();
52 }
53
54 bool open() override {
55 setState(QModbusDevice::ConnectedState);
56 return true;
57 }
58 void close() override {
59 setState(QModbusDevice::UnconnectedState);
60 }
61 QModbusResponse processRequest(const QModbusPdu &request) override
62 {
63 return QModbusServer::processRequest(request);
64 }
65};
66
67#define MAP_RANGE 500
68static QString s_msg;
69static void myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
70{
71 s_msg = msg;
72}
73
74class tst_QModbusServer : public QObject
75{
76 Q_OBJECT
77
78private:
79 TestServer server;
80
81private slots:
82 void init()
83 {
84 QModbusDataUnitMap map;
85 map.insert(key: QModbusDataUnit::DiscreteInputs, value: { QModbusDataUnit::DiscreteInputs, 0, MAP_RANGE });
86 map.insert(key: QModbusDataUnit::Coils, value: { QModbusDataUnit::Coils, 0, MAP_RANGE });
87 map.insert(key: QModbusDataUnit::InputRegisters, value: { QModbusDataUnit::InputRegisters, 0, MAP_RANGE });
88 map.insert(key: QModbusDataUnit::HoldingRegisters, value: { QModbusDataUnit::HoldingRegisters, 0, MAP_RANGE });
89 server.setMap(map);
90 }
91
92 void testServerAddress()
93 {
94 QCOMPARE(QModbusTcpServer().serverAddress(), 0xff);
95#if QT_CONFIG(modbus_serialport)
96 QCOMPARE(QModbusRtuSerialSlave().serverAddress(), 1);
97#endif
98 }
99
100 void testProcessRequestReadWriteSingleMultipleCoils()
101 {
102 // request write Coil 173, address: 0x00ac -> 172, value: 0xff00 -> ON
103 QModbusRequest request = QModbusRequest(QModbusRequest::WriteSingleCoil,
104 QByteArray::fromHex(hexEncoded: "00acff00"));
105 QModbusResponse response = server.processRequest(request);
106 QCOMPARE(response.isException(), false);
107 // response, equals request
108 QCOMPARE(response.data(), QByteArray::fromHex("00acff00"));
109
110 // request read Coil 173, address: 0x00ac -> 172, count: 0x0001 -> 1
111 request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex(hexEncoded: "00ac0001"));
112 response = server.processRequest(request);
113 QCOMPARE(response.isException(), false);
114 // response, byte count: 0x01 -> 1, status: 0x01 -> 0000 0001
115 QCOMPARE(response.data(), QByteArray::fromHex("0101"));
116
117 // request write 10 coils starting at coil 20, address: 0x0013 -> 19, count: 0x000a -> 10,
118 // payload bytes: 0x02 -> 2, values: 0xcd -> 1100 1101, 0x01 -> 0000 0001
119 request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
120 QByteArray::fromHex(hexEncoded: "0013000a02cd01"));
121 response = server.processRequest(request);
122 QCOMPARE(response.isException(), false);
123 // response, equals request's first 4 bytes
124 QCOMPARE(response.data(), QByteArray::fromHex("0013000a"));
125
126 // request read 10 coils starting at coil 20, address: 0x0013 -> 19, count: 0x000a -> 10
127 request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex(hexEncoded: "0013000a"));
128 response = server.processRequest(request);
129 QCOMPARE(response.isException(), false);
130 // response, byte count: 0x02 -> 1, status: 0xcd -> 1100 1101, 0x01 -> 0000 0001
131 QCOMPARE(response.data(), QByteArray::fromHex("02cd01"));
132
133 // request write 19 coils starting at coil 20, address: 0x0013 -> 19, count: 0x0013 -> 19,
134 // payload bytes: 0x03 -> 3, values: 0xcd -> 1100 1101, 0x6b -> 0110 1011, 0x05 -> 0000 0101
135 request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
136 QByteArray::fromHex(hexEncoded: "0013001303cd6b05"));
137 response = server.processRequest(request);
138 QCOMPARE(response.isException(), false);
139 // response, equals request's first 4 bytes
140 QCOMPARE(response.data(), QByteArray::fromHex("00130013"));
141
142 // request read 19 coils starting at coil 20, address: 0x0013 -> 19, count: 0x0013 -> 19
143 request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex(hexEncoded: "00130013"));
144 response = server.processRequest(request);
145 QCOMPARE(response.isException(), false);
146 // response, byte count: 0x03 -> 3
147 // status: 0xcd -> 1100 1101, 0x6b -> 0110 1011, 0x05 -> 0000 0101
148 QCOMPARE(response.data(), QByteArray::fromHex("03cd6b05"));
149
150 // request write 10 coils, starting at coil 0, address: 0x0000 -> 0, count: 0x000a -> 10
151 // payload bytes: 0x02 -> 2, values: 0xcd -> 1100 1101, 0x02 -> 0000 0010
152 request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
153 QByteArray::fromHex(hexEncoded: "0000000a02cd02"));
154 response = server.processRequest(request);
155 QCOMPARE(response.isException(), false);
156 // response, equals request's first 4 bytes
157 QCOMPARE(response.data(), QByteArray::fromHex("0000000a"));
158
159 // request read 10 coils starting at coil 0, address: 0x0000 -> 0, count: 0x000a -> 10
160 request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex(hexEncoded: "0000000a"));
161 response = server.processRequest(request);
162 QCOMPARE(response.isException(), false);
163 // response, byte count: 0x02 -> 2, status: 0xcd -> 1100 1101, 0x02 -> 0000 0020
164 QCOMPARE(response.data(), QByteArray::fromHex("02cd02"));
165 }
166
167 void testProcessReadDiscreteInputsRequest()
168 {
169 server.setData(table: QModbusDataUnit::DiscreteInputs, address: 172, data: true);
170 // request read DiscreteInput 173, address: 0x00ac -> 172, count: 0x0001 -> 1
171 QModbusRequest request = QModbusRequest(QModbusRequest::ReadDiscreteInputs,
172 QByteArray::fromHex(hexEncoded: "00ac0001"));
173 QModbusResponse response = server.processRequest(request);
174 QCOMPARE(response.isException(), false);
175 // response, byte count: 0x01 -> 1, status: 0x01 -> 0000 0001
176 QCOMPARE(response.data(), QByteArray::fromHex("0101"));
177
178 server.setData(table: QModbusDataUnit::DiscreteInputs, address: 19, data: quint16(0x0010));
179 // request read 10 inputs starting at input 20, address: 0x0013 -> 19, count: 0x000a -> 10
180 request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex(hexEncoded: "0013000a"));
181 response = server.processRequest(request);
182 QCOMPARE(response.isException(), false);
183 // response, byte count: 0x02 -> 1, status: 0x00 -> 0000 0000, 0x10 -> 0001 0000
184 QCOMPARE(response.data(), QByteArray::fromHex("020100"));
185
186 // request read 10 inputs starting at input 501
187 request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex(hexEncoded: "01f5000a"));
188 response = server.processRequest(request);
189 QCOMPARE(response.isException(), true);
190 QCOMPARE(response.data(), QByteArray::fromHex("02"));
191
192 // request read 2001 inputs starting at input 0
193 request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex(hexEncoded: "000007d1"));
194 response = server.processRequest(request);
195 QCOMPARE(response.isException(), true);
196 QCOMPARE(response.data(), QByteArray::fromHex("03"));
197
198 // request read 0 inputs starting at input 0
199 request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex(hexEncoded: "00000000"));
200 response = server.processRequest(request);
201 QCOMPARE(response.isException(), true);
202 QCOMPARE(response.data(), QByteArray::fromHex("03"));
203 }
204
205 void testProcessReadHoldingRegistersRequest()
206 {
207 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 172, data: 1234u);
208 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 173, data: 4321u);
209
210 // request read holding registers 173, address: 0x00ac -> 172, count: 0x0001 -> 1
211 QModbusRequest request = QModbusRequest(QModbusRequest::ReadHoldingRegisters,
212 QByteArray::fromHex(hexEncoded: "00ac0001"));
213 QModbusResponse response = server.processRequest(request);
214 QCOMPARE(response.isException(), false);
215 // response, byte count: 0x02 -> 2, value: 1234u -> 04d2
216 QCOMPARE(response.data(), QByteArray::fromHex("0204d2"));
217
218 // request read 2 registers starting at 172
219 request = QModbusRequest(QModbusRequest::ReadHoldingRegisters,
220 QByteArray::fromHex(hexEncoded: "00ac0002"));
221 response = server.processRequest(request);
222 QCOMPARE(response.isException(), false);
223 // response, byte count: 0x04 -> 4, status: 1234u = 04d2, 4321u =10e1
224 QCOMPARE(response.data(), QByteArray::fromHex("0404d210e1"));
225
226 // request read 10 registers starting at offset 501
227 request = QModbusRequest(QModbusRequest::ReadHoldingRegisters,
228 QByteArray::fromHex(hexEncoded: "01f5000a"));
229 response = server.processRequest(request);
230 QCOMPARE(response.isException(), true);
231 QCOMPARE(response.data(), QByteArray::fromHex("02"));
232
233 // request read 126 registers starting at offset 0
234 request = QModbusRequest(QModbusRequest::ReadHoldingRegisters, QByteArray::fromHex(hexEncoded: "0000007e"));
235 response = server.processRequest(request);
236 QCOMPARE(response.isException(), true);
237 QCOMPARE(response.data(), QByteArray::fromHex("03"));
238
239 // request read 0 registers starting at offset 0
240 request = QModbusRequest(QModbusRequest::ReadHoldingRegisters, QByteArray::fromHex(hexEncoded: "00000000"));
241 response = server.processRequest(request);
242 QCOMPARE(response.isException(), true);
243 QCOMPARE(response.data(), QByteArray::fromHex("03"));
244 }
245
246 void testProcessReadInputRegistersRequest()
247 {
248 server.setData(table: QModbusDataUnit::InputRegisters, address: 172, data: 1234u);
249 server.setData(table: QModbusDataUnit::InputRegisters, address: 173, data: 4321u);
250
251 // request read input registers 173, address: 0x00ac -> 172, count: 0x0001 -> 1
252 QModbusRequest request = QModbusRequest(QModbusRequest::ReadInputRegisters,
253 QByteArray::fromHex(hexEncoded: "00ac0001"));
254 QModbusResponse response = server.processRequest(request);
255 QCOMPARE(response.isException(), false);
256 // response, byte count: 0x02 -> 2, value: 1234u -> 04d2
257 QCOMPARE(response.data(), QByteArray::fromHex("0204d2"));
258
259 // request read 2 registers starting at 172
260 request = QModbusRequest(QModbusRequest::ReadInputRegisters,
261 QByteArray::fromHex(hexEncoded: "00ac0002"));
262 response = server.processRequest(request);
263 QCOMPARE(response.isException(), false);
264 // response, byte count: 0x04 -> 4, status: 1234u = 04d2, 4321u =10e1
265 QCOMPARE(response.data(), QByteArray::fromHex("0404d210e1"));
266
267 // request read 10 registers starting at offset 501
268 request = QModbusRequest(QModbusRequest::ReadInputRegisters,
269 QByteArray::fromHex(hexEncoded: "01f5000a"));
270 response = server.processRequest(request);
271 QCOMPARE(response.isException(), true);
272 QCOMPARE(response.data(), QByteArray::fromHex("02"));
273
274 // request read 1 register at offset 0 with corrupt message (+1 byte)
275 request = QModbusRequest(QModbusRequest::ReadInputRegisters,
276 QByteArray::fromHex(hexEncoded: "0000000100"));
277 response = server.processRequest(request);
278 QCOMPARE(response.isException(), true);
279 QCOMPARE(response.data(), QByteArray::fromHex("03"));
280
281 // request read 126 registers starting at offset 0
282 request = QModbusRequest(QModbusRequest::ReadInputRegisters, QByteArray::fromHex(hexEncoded: "0000007e"));
283 response = server.processRequest(request);
284 QCOMPARE(response.isException(), true);
285 QCOMPARE(response.data(), QByteArray::fromHex("03"));
286
287 // request read 0 registers starting at offset 0
288 request = QModbusRequest(QModbusRequest::ReadInputRegisters, QByteArray::fromHex(hexEncoded: "00000000"));
289 response = server.processRequest(request);
290 QCOMPARE(response.isException(), true);
291 QCOMPARE(response.data(), QByteArray::fromHex("03"));
292 }
293
294 void testProcessWriteSingleRegisterRequest()
295 {
296 // request write register 173, address: 0x00ac -> 172, value: 0x00ff
297 QModbusRequest request = QModbusRequest(QModbusRequest::WriteSingleRegister,
298 QByteArray::fromHex(hexEncoded: "00ac00ff"));
299 QModbusResponse response = server.processRequest(request);
300 QCOMPARE(response.isException(), false);
301 // response, equals request
302 QCOMPARE(response.data(), QByteArray::fromHex("00ac00ff"));
303
304 // request write register at offset 501
305 request = QModbusRequest(QModbusRequest::WriteSingleRegister,
306 QByteArray::fromHex(hexEncoded: "01f500ff"));
307 response = server.processRequest(request);
308 QCOMPARE(response.isException(), true);
309 QCOMPARE(response.data(), QByteArray::fromHex("02"));
310
311 // request write register at offset 0 value only 1 byte
312 request = QModbusRequest(QModbusRequest::WriteSingleRegister,
313 QByteArray::fromHex(hexEncoded: "00007d"));
314 response = server.processRequest(request);
315 QCOMPARE(response.isException(), true);
316 QCOMPARE(response.data(), QByteArray::fromHex("03"));
317 }
318
319 void testProcessReadExceptionStatus()
320 {
321 // simulate Modicon 484 (start at coil 257 up to 264)
322 server.setValue(option: QModbusServer::ExceptionStatusOffset, value: 256u);
323 // set the exception status byte to 0000 0011 simulating two bits set
324 // request write Coil 257, address: 0x0100 -> 256, value: 0xff00 -> ON
325 QModbusRequest request = QModbusRequest(QModbusRequest::WriteSingleCoil,
326 QByteArray::fromHex(hexEncoded: "0100ff00"));
327 QModbusResponse response = server.processRequest(request);
328 // request write Coil 258, address: 0x0101 -> 257, value: 0xff00 -> ON
329 request = QModbusRequest(QModbusRequest::WriteSingleCoil,
330 QByteArray::fromHex(hexEncoded: "0101ff00"));
331 response = server.processRequest(request);
332
333 request = QModbusRequest(QModbusRequest::ReadExceptionStatus);
334 response = server.processRequest(request);
335 QCOMPARE(response.isException(), false);
336 // response, equals request
337 QCOMPARE(response.data(), QByteArray::fromHex("03"));
338
339 // invalid request test
340 request = QModbusRequest(QModbusRequest::ReadExceptionStatus,
341 QByteArray::fromHex(hexEncoded: "007d"));
342 response = server.processRequest(request);
343 QCOMPARE(response.isException(), true);
344 QCOMPARE(response.data(), QByteArray::fromHex("03"));
345 }
346
347 void testProcessDiagnosticsRequest()
348 {
349 // subfunction 00
350 QModbusRequest request = QModbusRequest(QModbusRequest::Diagnostics,
351 QByteArray::fromHex(hexEncoded: "000000ffabcd"));
352 QModbusResponse response = server.processRequest(request);
353 QCOMPARE(response.isException(), false);
354 // response, equals request
355 QCOMPARE(response.data(), QByteArray::fromHex("000000ffabcd"));
356
357 //subfunction 01
358 request = QModbusRequest(QModbusRequest::Diagnostics,
359 QByteArray::fromHex(hexEncoded: "00010000"));
360 response = server.processRequest(request);
361 QCOMPARE(response.isException(), false);
362 // response, equals request
363 QCOMPARE(response.data(), QByteArray::fromHex("00010000"));
364
365 request = QModbusRequest(QModbusRequest::Diagnostics,
366 QByteArray::fromHex(hexEncoded: "0001ff00"));
367 response = server.processRequest(request);
368 QCOMPARE(response.isException(), false);
369 // response, equals request
370 QCOMPARE(response.data(), QByteArray::fromHex("0001ff00"));
371
372 // invalidate
373 request = QModbusRequest(QModbusRequest::Diagnostics,
374 QByteArray::fromHex(hexEncoded: "0001ff01"));
375 response = server.processRequest(request);
376 QCOMPARE(response.isException(), true);
377 // response, equals request
378 QCOMPARE(response.data(), QByteArray::fromHex("03"));
379
380 // subfunction 02
381 // validate
382 request = QModbusRequest(QModbusRequest::Diagnostics,
383 QByteArray::fromHex(hexEncoded: "00020000"));
384 response = server.processRequest(request);
385 QCOMPARE(response.isException(), false);
386 // response, equals request
387 QCOMPARE(response.data(), QByteArray::fromHex("00020000"));
388
389 // invalidate
390 request = QModbusRequest(QModbusRequest::Diagnostics,
391 QByteArray::fromHex(hexEncoded: "00020001"));
392 response = server.processRequest(request);
393 QCOMPARE(response.isException(), true);
394 // response, equals request
395 QCOMPARE(response.data(), QByteArray::fromHex("03"));
396
397 request = QModbusRequest(QModbusRequest::Diagnostics,
398 QByteArray::fromHex(hexEncoded: "0002000001"));
399 response = server.processRequest(request);
400 QCOMPARE(response.isException(), true);
401 // response, equals request
402 QCOMPARE(response.data(), QByteArray::fromHex("03"));
403
404 // subfunction 03
405 // validate
406 request = QModbusRequest(QModbusRequest::Diagnostics,
407 QByteArray::fromHex(hexEncoded: "00030a00"));
408 response = server.processRequest(request);
409 QCOMPARE(response.isException(), false);
410 // response, equals request
411 QCOMPARE(response.data(), QByteArray::fromHex("00030a00"));
412
413 // invalidate
414 request = QModbusRequest(QModbusRequest::Diagnostics,
415 QByteArray::fromHex(hexEncoded: "00030a01"));
416 response = server.processRequest(request);
417 QCOMPARE(response.isException(), true);
418 // response, equals request
419 QCOMPARE(response.data(), QByteArray::fromHex("03"));
420
421 request = QModbusRequest(QModbusRequest::Diagnostics,
422 QByteArray::fromHex(hexEncoded: "00030a0101"));
423 response = server.processRequest(request);
424 QCOMPARE(response.isException(), true);
425 // response, equals request
426 QCOMPARE(response.data(), QByteArray::fromHex("03"));
427
428 // subfunction 04
429 request = QModbusRequest(QModbusRequest::Diagnostics,
430 QByteArray::fromHex(hexEncoded: "00040000"));
431 response = server.processRequest(request);
432 QCOMPARE(response.isValid(), false);
433 QCOMPARE(response.isException(), false);
434 QCOMPARE(response.data(), QByteArray());
435 QCOMPARE(response.functionCode(), QModbusResponse::Invalid);
436
437 // invalidate
438 request = QModbusRequest(QModbusRequest::Diagnostics,
439 QByteArray::fromHex(hexEncoded: "00040001"));
440 response = server.processRequest(request);
441 QCOMPARE(response.isException(), true);
442 // response, equals request
443 QCOMPARE(response.data(), QByteArray::fromHex("03"));
444
445 request = QModbusRequest(QModbusRequest::Diagnostics,
446 QByteArray::fromHex(hexEncoded: "0004000001"));
447 response = server.processRequest(request);
448 QCOMPARE(response.isException(), true);
449 // response, equals request
450 QCOMPARE(response.data(), QByteArray::fromHex("03"));
451
452 // subfunction 10: counter value is 0
453 request = QModbusRequest(QModbusRequest::Diagnostics,
454 QByteArray::fromHex(hexEncoded: "000a0000"));
455 response = server.processRequest(request);
456 QCOMPARE(response.isException(), false);
457 // response, equals request
458 QCOMPARE(response.data(), QByteArray::fromHex("000a0000"));
459
460 // invalidate
461 request = QModbusRequest(QModbusRequest::Diagnostics,
462 QByteArray::fromHex(hexEncoded: "000a0001"));
463 response = server.processRequest(request);
464 QCOMPARE(response.isException(), true);
465 // response, equals request
466 QCOMPARE(response.data(), QByteArray::fromHex("03"));
467
468 request = QModbusRequest(QModbusRequest::Diagnostics,
469 QByteArray::fromHex(hexEncoded: "000a000001"));
470 response = server.processRequest(request);
471 QCOMPARE(response.isException(), true);
472 // response, equals request
473 QCOMPARE(response.data(), QByteArray::fromHex("03"));
474
475 // subfunction 11: counter value is 0
476 request = QModbusRequest(QModbusRequest::Diagnostics,
477 QByteArray::fromHex(hexEncoded: "000b0000"));
478 response = server.processRequest(request);
479 QCOMPARE(response.isException(), false);
480 // response, equals request
481 QCOMPARE(response.data(), QByteArray::fromHex("000b0000"));
482
483 // invalidate
484 request = QModbusRequest(QModbusRequest::Diagnostics,
485 QByteArray::fromHex(hexEncoded: "000b0001"));
486 response = server.processRequest(request);
487 QCOMPARE(response.isException(), true);
488 // response, equals request
489 QCOMPARE(response.data(), QByteArray::fromHex("03"));
490
491 request = QModbusRequest(QModbusRequest::Diagnostics,
492 QByteArray::fromHex(hexEncoded: "000b000001"));
493 response = server.processRequest(request);
494 QCOMPARE(response.isException(), true);
495 // response, equals request
496 QCOMPARE(response.data(), QByteArray::fromHex("03"));
497
498 // subfunction 12: counter value is 0
499 request = QModbusRequest(QModbusRequest::Diagnostics,
500 QByteArray::fromHex(hexEncoded: "000c0000"));
501 response = server.processRequest(request);
502 QCOMPARE(response.isException(), false);
503 // response, equals request
504 QCOMPARE(response.data(), QByteArray::fromHex("000c0000"));
505
506 // invalidate
507 request = QModbusRequest(QModbusRequest::Diagnostics,
508 QByteArray::fromHex(hexEncoded: "000c0001"));
509 response = server.processRequest(request);
510 QCOMPARE(response.isException(), true);
511 // response, equals request
512 QCOMPARE(response.data(), QByteArray::fromHex("03"));
513
514 request = QModbusRequest(QModbusRequest::Diagnostics,
515 QByteArray::fromHex(hexEncoded: "000c000001"));
516 response = server.processRequest(request);
517 QCOMPARE(response.isException(), true);
518 // response, equals request
519 QCOMPARE(response.data(), QByteArray::fromHex("03"));
520
521 // subfunction 13: counter value is 0
522 request = QModbusRequest(QModbusRequest::Diagnostics,
523 QByteArray::fromHex(hexEncoded: "000d0000"));
524 response = server.processRequest(request);
525 QCOMPARE(response.isException(), false);
526 // response, equals request
527 QCOMPARE(response.data(), QByteArray::fromHex("000d0000"));
528
529 // invalidate
530 request = QModbusRequest(QModbusRequest::Diagnostics,
531 QByteArray::fromHex(hexEncoded: "000d0001"));
532 response = server.processRequest(request);
533 QCOMPARE(response.isException(), true);
534 // response, equals request
535 QCOMPARE(response.data(), QByteArray::fromHex("03"));
536
537 request = QModbusRequest(QModbusRequest::Diagnostics,
538 QByteArray::fromHex(hexEncoded: "000d000001"));
539 response = server.processRequest(request);
540 QCOMPARE(response.isException(), true);
541 // response, equals request
542 QCOMPARE(response.data(), QByteArray::fromHex("03"));
543
544 // subfunction 14: counter value is 0
545 request = QModbusRequest(QModbusRequest::Diagnostics,
546 QByteArray::fromHex(hexEncoded: "000e0000"));
547 response = server.processRequest(request);
548 QCOMPARE(response.isException(), false);
549 // response, equals request
550 QCOMPARE(response.data(), QByteArray::fromHex("000e0000"));
551
552 // invalidate
553 request = QModbusRequest(QModbusRequest::Diagnostics,
554 QByteArray::fromHex(hexEncoded: "000e0001"));
555 response = server.processRequest(request);
556 QCOMPARE(response.isException(), true);
557 // response, equals request
558 QCOMPARE(response.data(), QByteArray::fromHex("03"));
559
560 request = QModbusRequest(QModbusRequest::Diagnostics,
561 QByteArray::fromHex(hexEncoded: "000e000001"));
562 response = server.processRequest(request);
563 QCOMPARE(response.isException(), true);
564 // response, equals request
565 QCOMPARE(response.data(), QByteArray::fromHex("03"));
566
567 // subfunction 15: counter value is 0
568 request = QModbusRequest(QModbusRequest::Diagnostics,
569 QByteArray::fromHex(hexEncoded: "000f0000"));
570 response = server.processRequest(request);
571 QCOMPARE(response.isException(), false);
572 // response, equals request
573 QCOMPARE(response.data(), QByteArray::fromHex("000f0000"));
574
575 // invalidate
576 request = QModbusRequest(QModbusRequest::Diagnostics,
577 QByteArray::fromHex(hexEncoded: "000f0001"));
578 response = server.processRequest(request);
579 QCOMPARE(response.isException(), true);
580 // response, equals request
581 QCOMPARE(response.data(), QByteArray::fromHex("03"));
582
583 request = QModbusRequest(QModbusRequest::Diagnostics,
584 QByteArray::fromHex(hexEncoded: "000f000001"));
585 response = server.processRequest(request);
586 QCOMPARE(response.isException(), true);
587 // response, equals request
588 QCOMPARE(response.data(), QByteArray::fromHex("03"));
589
590 // subfunction 16: counter value is 0
591 request = QModbusRequest(QModbusRequest::Diagnostics,
592 QByteArray::fromHex(hexEncoded: "00100000"));
593 response = server.processRequest(request);
594 QCOMPARE(response.isException(), false);
595 // response, equals request
596 QCOMPARE(response.data(), QByteArray::fromHex("00100000"));
597
598 // invalidate
599 request = QModbusRequest(QModbusRequest::Diagnostics,
600 QByteArray::fromHex(hexEncoded: "00100001"));
601 response = server.processRequest(request);
602 QCOMPARE(response.isException(), true);
603 // response, equals request
604 QCOMPARE(response.data(), QByteArray::fromHex("03"));
605
606 request = QModbusRequest(QModbusRequest::Diagnostics,
607 QByteArray::fromHex(hexEncoded: "0010000001"));
608 response = server.processRequest(request);
609 QCOMPARE(response.isException(), true);
610 // response, equals request
611 QCOMPARE(response.data(), QByteArray::fromHex("03"));
612
613 // subfunction 17: counter value is 0
614 request = QModbusRequest(QModbusRequest::Diagnostics,
615 QByteArray::fromHex(hexEncoded: "00110000"));
616 response = server.processRequest(request);
617 QCOMPARE(response.isException(), false);
618 // response, equals request
619 QCOMPARE(response.data(), QByteArray::fromHex("00110000"));
620
621 // invalidate
622 request = QModbusRequest(QModbusRequest::Diagnostics,
623 QByteArray::fromHex(hexEncoded: "00110001"));
624 response = server.processRequest(request);
625 QCOMPARE(response.isException(), true);
626 // response, equals request
627 QCOMPARE(response.data(), QByteArray::fromHex("03"));
628
629 request = QModbusRequest(QModbusRequest::Diagnostics,
630 QByteArray::fromHex(hexEncoded: "0011000001"));
631 response = server.processRequest(request);
632 QCOMPARE(response.isException(), true);
633 // response, equals request
634 QCOMPARE(response.data(), QByteArray::fromHex("03"));
635
636 // subfunction 18: counter value is 0
637 request = QModbusRequest(QModbusRequest::Diagnostics,
638 QByteArray::fromHex(hexEncoded: "00120000"));
639 response = server.processRequest(request);
640 QCOMPARE(response.isException(), false);
641 // response, equals request
642 QCOMPARE(response.data(), QByteArray::fromHex("00120000"));
643
644 // invalidate
645 request = QModbusRequest(QModbusRequest::Diagnostics,
646 QByteArray::fromHex(hexEncoded: "00120001"));
647 response = server.processRequest(request);
648 QCOMPARE(response.isException(), true);
649 // response, equals request
650 QCOMPARE(response.data(), QByteArray::fromHex("03"));
651
652 request = QModbusRequest(QModbusRequest::Diagnostics,
653 QByteArray::fromHex(hexEncoded: "0012000001"));
654 response = server.processRequest(request);
655 QCOMPARE(response.isException(), true);
656 // response, equals request
657 QCOMPARE(response.data(), QByteArray::fromHex("03"));
658
659 // subfunction > 4 < 10
660 request = QModbusRequest(QModbusRequest::Diagnostics,
661 QByteArray::fromHex(hexEncoded: "0005ff01"));
662 response = server.processRequest(request);
663 QCOMPARE(response.isException(), true);
664 // response, equals request
665 QCOMPARE(response.data(), QByteArray::fromHex("01"));
666
667 // subfunction > 18
668 request = QModbusRequest(QModbusRequest::Diagnostics,
669 QByteArray::fromHex(hexEncoded: "00130000"));
670 response = server.processRequest(request);
671 QCOMPARE(response.isException(), true);
672 // response, equals request
673 QCOMPARE(response.data(), QByteArray::fromHex("01"));
674 }
675
676 void testProcessGetCommEventCounter()
677 {
678 TestServer local; // Used later to control the correct event amount.
679
680 QModbusRequest request = QModbusRequest(QModbusRequest::GetCommEventCounter);
681 QModbusResponse response = local.processRequest(request);
682 QCOMPARE(response.isException(), false);
683 QCOMPARE(response.data(), QByteArray::fromHex("00000000"));
684
685 // TODO: Add more tests once event handling is implemented.
686
687 request = QModbusRequest(QModbusRequest::GetCommEventCounter, quint8(10));
688 response = local.processRequest(request);
689 QCOMPARE(response.isException(), true);
690
691 }
692
693 void testProcessGetCommEventLogRequest()
694 {
695 TestServer local; // Used later to control the correct event amount.
696
697 QModbusRequest request = QModbusRequest(QModbusRequest::GetCommEventLog);
698 QModbusResponse response = local.processRequest(request);
699 QCOMPARE(response.isException(), false);
700 QCOMPARE(response.data(), QByteArray::fromHex("06000000000000"));
701
702 // TODO: Add more tests once event handling is implemented.
703
704 request = QModbusRequest(QModbusRequest::GetCommEventLog, quint8(10));
705 response = local.processRequest(request);
706 QCOMPARE(response.isException(), true);
707 }
708
709 void testProcessWriteMultipleRegistersRequest()
710 {
711 // request write at register 173, address: 0x00ac -> 172, value: 0x00ff 0x1234
712 QModbusRequest request = QModbusRequest(QModbusRequest::WriteMultipleRegisters,
713 QByteArray::fromHex(hexEncoded: "00ac00020400ff1234"));
714 QModbusResponse response = server.processRequest(request);
715 QCOMPARE(response.isException(), false);
716 // response, equals request
717 QCOMPARE(response.data(), QByteArray::fromHex("00ac0002"));
718
719 // request write register at offset 501
720 request = QModbusRequest(QModbusRequest::WriteMultipleRegisters,
721 QByteArray::fromHex(hexEncoded: "01f500010200ff"));
722 response = server.processRequest(request);
723 QCOMPARE(response.isException(), true);
724 QCOMPARE(response.data(), QByteArray::fromHex("02"));
725
726 // request write 1 register at offset 0 value only 1 byte
727 request = QModbusRequest(QModbusRequest::WriteMultipleRegisters,
728 QByteArray::fromHex(hexEncoded: "000000010200"));
729 response = server.processRequest(request);
730 QCOMPARE(response.isException(), true);
731 QCOMPARE(response.data(), QByteArray::fromHex("03"));
732 }
733
734 void testReportServerId()
735 {
736 QModbusRequest request = QModbusRequest(QModbusRequest::ReportServerId);
737 QModbusResponse response = server.processRequest(request);
738 QCOMPARE(response.isException(), false);
739 QCOMPARE(response.functionCode(), QModbusRequest::ReportServerId);
740
741 const QByteArray additionalData = "Qt Modbus Server";
742 QCOMPARE(server.value(QModbusServer::ServerIdentifier).value<quint8>(), quint8(0x0a));
743 QCOMPARE(server.value(QModbusServer::RunIndicatorStatus).value<quint8>(), quint8(0xff));
744 QCOMPARE(server.value(QModbusServer::AdditionalData).toByteArray(), additionalData);
745
746 QByteArray data = QByteArray::fromHex(hexEncoded: "0aff") + additionalData;
747 data.prepend(c: quint8(data.size()));
748 QCOMPARE(response.data(), data);
749
750 request = QModbusRequest(QModbusRequest::ReportServerId, data);
751 response = server.processRequest(request);
752 QCOMPARE(response.isException(), true);
753 QCOMPARE(response.exceptionCode(), QModbusPdu::IllegalDataValue);
754
755 server.setValue(option: QModbusServer::ServerIdentifier, value: quint8(0xff));
756 QCOMPARE(server.setValue(QModbusServer::ServerIdentifier, additionalData), false);
757 QCOMPARE(server.value(QModbusServer::ServerIdentifier).value<quint8>(), quint8(0xff));
758
759 server.setValue(option: QModbusServer::RunIndicatorStatus, value: quint8(0x00));
760 QCOMPARE(server.setValue(QModbusServer::RunIndicatorStatus, quint8(0xab)), false);
761 QCOMPARE(server.value(QModbusServer::RunIndicatorStatus).value<quint8>(), quint8(0x00));
762
763 server.setValue(option: QModbusServer::AdditionalData, value: QByteArray("TestData"));
764 QCOMPARE(server.setValue(QModbusServer::AdditionalData, QStringList()), false);
765 QCOMPARE(server.value(QModbusServer::AdditionalData).toByteArray(), QByteArray("TestData"));
766 }
767
768 void testMaskWriteRegister()
769 {
770 // preset register 172 with value 18 (0x0012h)
771 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 172, data: 18u);
772 //mask request register 172 with andMask: 242 (0x00f2)
773 // orMask: 37 (0x0025)
774 // result: 23 (0x0017)
775 QModbusRequest request = QModbusRequest(QModbusRequest::MaskWriteRegister,
776 QByteArray::fromHex(hexEncoded: "00ac00f20025"));
777 QModbusResponse response = server.processRequest(request);
778 QCOMPARE(response.isException(), false);
779 // response, equals request
780 QCOMPARE(response.data(), QByteArray::fromHex("00ac00f20025"));
781 // validate contents after masking
782 quint16 data;
783 QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 172, &data));
784 QCOMPARE(data, quint16(23));
785
786 // invalidate use register 501:
787 request = QModbusRequest(QModbusRequest::MaskWriteRegister,
788 QByteArray::fromHex(hexEncoded: "01f500f20025"));
789 response = server.processRequest(request);
790 QCOMPARE(response.isException(), true);
791 QCOMPARE(response.data(), QByteArray::fromHex("02"));
792 // invalidate with one bytes less data:
793 request = QModbusRequest(QModbusRequest::MaskWriteRegister,
794 QByteArray::fromHex(hexEncoded: "00ac00f200"));
795 response = server.processRequest(request);
796 QCOMPARE(response.isException(), true);
797 QCOMPARE(response.data(), QByteArray::fromHex("03"));
798 // invalidate with one bytes more data:
799 request = QModbusRequest(QModbusRequest::MaskWriteRegister,
800 QByteArray::fromHex(hexEncoded: "00ac00f2002500"));
801 response = server.processRequest(request);
802 QCOMPARE(response.isException(), true);
803 QCOMPARE(response.data(), QByteArray::fromHex("03"));
804 }
805
806 void testProcessReadWriteMultipleRegistersRequest()
807 {
808 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 172, data: 1234u);
809 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 173, data: 1235u);
810 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 174, data: 1236u);
811
812 // request read 3 registers at register 173, address: 0x00ac -> 172,
813 // write 3 registers at register 173: 1237u, 1238u, 1239u
814 QModbusRequest request = QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters,
815 QByteArray::fromHex(hexEncoded: "00ac000300ac00030604d504d604d7"));
816 QModbusResponse response = server.processRequest(request);
817 QCOMPARE(response.isException(), false);
818 // response, equals request
819 QCOMPARE(response.data(), QByteArray::fromHex("0604d504d604d7"));
820
821 // request write register at offset 501
822 request = QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters,
823 QByteArray::fromHex(hexEncoded: "00ac000301f500010200ff"));
824 response = server.processRequest(request);
825 QCOMPARE(response.isException(), true);
826 QCOMPARE(response.data(), QByteArray::fromHex("02"));
827
828 // request write 3 registers at offset 173 value only 5 bytes
829 request = QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters,
830 QByteArray::fromHex(hexEncoded: "00ac000300ac00030604d504d604"));
831 response = server.processRequest(request);
832 QCOMPARE(response.isException(), true);
833 QCOMPARE(response.data(), QByteArray::fromHex("03"));
834 }
835
836 void testProcessReadFifoQueue()
837 {
838 // prepare a fifo with two object values, pointer address is 172 with value 2 items
839 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 172, data: 2u);
840 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 173, data: 1235u);
841 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 174, data: 1236u);
842 // request read fifo queue at fifo pointer address 172
843 QModbusRequest request = QModbusRequest(QModbusRequest::ReadFifoQueue,
844 QByteArray::fromHex(hexEncoded: "00ac"));
845 QModbusResponse response = server.processRequest(request);
846 QCOMPARE(response.isException(), false);
847 QCOMPARE(response.data(), QByteArray::fromHex("0006000204d304d4"));
848 // invalidate tests
849 // invalid offset address (501)
850 request = QModbusRequest(QModbusRequest::ReadFifoQueue,
851 QByteArray::fromHex(hexEncoded: "01f5"));
852 response = server.processRequest(request);
853 QCOMPARE(response.isException(), true);
854 QCOMPARE(response.data(), QByteArray::fromHex("02"));
855 // invalid fifo count > 31
856 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 172, data: 32u);
857 request = QModbusRequest(QModbusRequest::ReadFifoQueue,
858 QByteArray::fromHex(hexEncoded: "00ac"));
859 response = server.processRequest(request);
860 QCOMPARE(response.isException(), true);
861 QCOMPARE(response.data(), QByteArray::fromHex("03"));
862 // invalid fifo data address beyond 500, fifo values 3 (500-502)
863 server.setData(table: QModbusDataUnit::HoldingRegisters, address: 499, data: 3u);
864 request = QModbusRequest(QModbusRequest::ReadFifoQueue,
865 QByteArray::fromHex(hexEncoded: "01f3"));
866 response = server.processRequest(request);
867 QCOMPARE(response.isException(), true);
868 QCOMPARE(response.data(), QByteArray::fromHex("02"));
869 }
870
871 void tst_dataCalls_data()
872 {
873 QTest::addColumn<QModbusDataUnit::RegisterType>(name: "registerType");
874
875 QTest::newRow(dataTag: "InitCoils") << QModbusDataUnit::Coils;
876 QTest::newRow(dataTag: "InitDiscreteInputs") << QModbusDataUnit::DiscreteInputs;
877 QTest::newRow(dataTag: "InitInputRegisters") << QModbusDataUnit::InputRegisters;
878 QTest::newRow(dataTag: "InitHoldingRegisters") << QModbusDataUnit::HoldingRegisters;
879 QTest::newRow(dataTag: "InitInvalid") << QModbusDataUnit::Invalid;
880 }
881
882 void tst_dataCalls()
883 {
884 QFETCH(QModbusDataUnit::RegisterType, registerType);
885 // basic assumption, all registers have start address 0 and size 500
886
887 const bool validDataUnit = (registerType != QModbusDataUnit::Invalid);
888 //test initialization of 0
889 if (validDataUnit) {
890 for (int i = 0; i < MAP_RANGE; i++) {
891 quint16 data = 123;
892 QVERIFY(server.data(registerType, i, &data));
893 QCOMPARE(data, quint16(0));
894 }
895 } else {
896 quint16 data = 0;
897 QVERIFY(!server.data(registerType, 0 , &data));
898 }
899
900 quint16 data = 0;
901 QSignalSpy writtenSpy(
902 &server, SIGNAL(dataWritten(QModbusDataUnit::RegisterType,int,int)));
903 QVERIFY(writtenSpy.isEmpty());
904
905 QVERIFY(!server.data(registerType, MAP_RANGE+1, &data)); // out of range
906 QCOMPARE(data, quint16(0));
907 QVERIFY(!server.data(registerType, 1, 0)); // invalid data pointer
908 QCOMPARE(data, quint16(0));
909
910 QCOMPARE(server.setData(registerType, 1, 444), validDataUnit);
911 QCOMPARE(server.data(registerType, 1, &data), validDataUnit);
912 if (validDataUnit) {
913 QCOMPARE(data, quint16(444));
914 QTRY_COMPARE(writtenSpy.count(), 1);
915 QList<QVariant> signalData = writtenSpy.at(i: 0);
916 QCOMPARE(signalData.count(), 3);
917 QCOMPARE(signalData.at(0).value<QModbusDataUnit::RegisterType>(), registerType);
918 QCOMPARE(signalData.at(1).toInt(), 1);
919 QCOMPARE(signalData.at(2).toInt(), 1);
920 } else {
921 QCOMPARE(data, quint16(0));
922 QTRY_VERIFY(writtenSpy.isEmpty());
923 }
924
925 //write 444 again but this time no dataWritten since no value change
926 data = 0;
927 writtenSpy.clear();
928 QCOMPARE(server.setData(registerType, 1, 444), validDataUnit);
929 QCOMPARE(server.data(registerType, 1, &data), validDataUnit);
930 if (validDataUnit)
931 QCOMPARE(data, quint16(444));
932 else
933 QCOMPARE(data, quint16(0));
934 QTRY_VERIFY(writtenSpy.isEmpty()); //
935
936 QVERIFY(!server.data(registerType, 1, 0)); // out of range although value set
937 QVERIFY(!server.setData(registerType, -1, 1));
938 QVERIFY(!server.setData(registerType, MAP_RANGE+1, 1));
939 QTRY_VERIFY(writtenSpy.isEmpty());
940
941 //testing server.setData(ModbusDataUnit&)
942
943 const QVector<quint16> valueVector = { 1, 1, 1, 1, 1};
944 const QVector<quint16> zeroVector = { 0, 0, 0, 0, 0};
945 QModbusDataUnit rangeUnit(registerType, 7, valueVector);
946 QCOMPARE(rangeUnit.valueCount(), 5u);
947 QCOMPARE(rangeUnit.values().count(), 5);
948 QCOMPARE(rangeUnit.startAddress(), 7);
949 QVERIFY(rangeUnit.values() == valueVector);
950 QVERIFY(rangeUnit.registerType() == registerType);
951
952 writtenSpy.clear();
953 QVERIFY(server.setData(rangeUnit) == validDataUnit);
954 if (validDataUnit) {
955 for (int i = rangeUnit.startAddress();
956 i < rangeUnit.startAddress() + int(rangeUnit.valueCount()); i++) {
957 quint16 readData = 0;
958 QVERIFY(server.data(registerType, i, &readData));
959 QCOMPARE(readData, valueVector.at(i-rangeUnit.startAddress()));
960 }
961
962 QTRY_COMPARE(writtenSpy.count(), 1);
963 QList<QVariant> signalData = writtenSpy.at(i: 0);
964 QCOMPARE(signalData.count(), 3);
965 QCOMPARE(signalData.at(0).value<QModbusDataUnit::RegisterType>(), registerType);
966 QCOMPARE(signalData.at(1).toInt(), rangeUnit.startAddress());
967 QCOMPARE(signalData.at(2).toUInt(), rangeUnit.valueCount());
968 }
969
970 // no writtenData() signal when writing same rangeUnit again
971 writtenSpy.clear();
972 QVERIFY(server.setData(rangeUnit) == validDataUnit);
973 QTRY_VERIFY(writtenSpy.isEmpty());
974
975 //never fits anywhere
976 QModbusDataUnit oversizeUnit(registerType, 0, MAP_RANGE*2);
977 QCOMPARE(oversizeUnit.valueCount(), uint(MAP_RANGE*2));
978 QCOMPARE(oversizeUnit.values().count(), MAP_RANGE*2);
979 QCOMPARE(oversizeUnit.startAddress(), 0);
980 QCOMPARE(oversizeUnit.registerType(), registerType);
981
982 //completely outside of valid range
983 writtenSpy.clear();
984 rangeUnit.setStartAddress(MAP_RANGE + 1 );
985 QVERIFY(!server.setData(rangeUnit));
986
987 //slightly outside of valid range
988 rangeUnit.setStartAddress(MAP_RANGE - 2);
989 QVERIFY(!server.setData(rangeUnit));
990
991 //slightly outside of valid range in the bottom
992 rangeUnit.setStartAddress(-1);
993 QVERIFY(!server.setData(rangeUnit));
994
995 //input data unit doesn't fit
996 QVERIFY(!server.setData(oversizeUnit));
997 oversizeUnit.setStartAddress(-1);
998 QVERIFY(!server.setData(oversizeUnit));
999 oversizeUnit.setStartAddress(MAP_RANGE+1);
1000 QVERIFY(!server.setData(oversizeUnit));
1001 oversizeUnit.setStartAddress(MAP_RANGE-2);
1002 QVERIFY(!server.setData(oversizeUnit));
1003 QTRY_VERIFY(writtenSpy.isEmpty());
1004
1005 //testing server.data(QModbusDataUnit *)
1006 QModbusDataUnit requestUnit(registerType, 7, 5);
1007 QCOMPARE(requestUnit.valueCount(), 5u);
1008 QCOMPARE(requestUnit.values().count(), 5);
1009 QCOMPARE(requestUnit.startAddress(), 7);
1010 QVERIFY(requestUnit.registerType() == registerType);
1011 QVERIFY(requestUnit.values() != valueVector);
1012
1013 QVERIFY(server.data(&requestUnit) == validDataUnit);
1014 if (validDataUnit) {
1015 QVERIFY(requestUnit.values() == valueVector);
1016 QCOMPARE(requestUnit.valueCount(), 5u);
1017 QCOMPARE(requestUnit.values().count(), 5);
1018 QCOMPARE(requestUnit.startAddress(), 7);
1019 }
1020
1021 requestUnit.setValues(zeroVector);
1022 QVERIFY(requestUnit.values() != valueVector);
1023 QVERIFY(requestUnit.values() == zeroVector);
1024
1025 requestUnit.setStartAddress(MAP_RANGE + 1);
1026 QVERIFY(!server.data(&requestUnit));
1027 requestUnit.setStartAddress(MAP_RANGE - 2);
1028 QVERIFY(!server.data(&requestUnit));
1029
1030 // ask for entire map
1031 requestUnit.setStartAddress(-1);
1032 QVERIFY(server.data(&requestUnit) == validDataUnit);
1033 if (validDataUnit) {
1034 QCOMPARE(requestUnit.valueCount(), uint(MAP_RANGE));
1035 QCOMPARE(requestUnit.values().count(), MAP_RANGE);
1036 }
1037
1038 oversizeUnit.setStartAddress(0);
1039 QVERIFY(!server.data(&oversizeUnit));
1040 oversizeUnit.setStartAddress(MAP_RANGE+1);
1041 QVERIFY(!server.data(&oversizeUnit));
1042 oversizeUnit.setStartAddress(MAP_RANGE-2);
1043 QVERIFY(!server.data(&oversizeUnit));
1044
1045 oversizeUnit.setStartAddress(-1);
1046 QVERIFY(server.data(&oversizeUnit) == validDataUnit);
1047 if (validDataUnit) {
1048 QCOMPARE(oversizeUnit.valueCount(), uint(MAP_RANGE));
1049 QCOMPARE(oversizeUnit.values().count(), MAP_RANGE);
1050 }
1051 }
1052
1053 void tst_dataCallsShiftedIndex()
1054 {
1055 TestServer overlapIndex;
1056 const quint16 dataCount = 4;
1057
1058 QModbusDataUnitMap map;
1059 map.insert(key: QModbusDataUnit::HoldingRegisters, value: {QModbusDataUnit::HoldingRegisters, 3, dataCount});
1060 server.setMap(map);
1061 QCOMPARE(map.value(QModbusDataUnit::HoldingRegisters).valueCount(), dataCount);
1062
1063 QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 3, 0xaaaa));
1064 QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 4, 0xbbbb));
1065 QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 5, 0xcccc));
1066 QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 6, 0xdddd));
1067
1068 // ********** Test individual access ********** //
1069 quint16 data = 0;
1070 QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 3, &data));
1071 QCOMPARE(data, 0xaaaa);
1072 QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 4, &data));
1073 QCOMPARE(data, 0xbbbb);
1074 QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 5, &data));
1075 QCOMPARE(data, 0xcccc);
1076 QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 6, &data));
1077 QCOMPARE(data, 0xdddd);
1078
1079
1080 // block write at start
1081 QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 3, 3);
1082 for (int i = 0; i < 3; i++)
1083 unit.setValue(index: i, newValue: quint16(0x1111 + i));
1084 QVERIFY(server.setData(unit));
1085
1086 QModbusDataUnit results(QModbusDataUnit::HoldingRegisters, 3, 3);
1087 QVERIFY(server.data(&results));
1088 QCOMPARE(results.values(), QVector<quint16>({0x1111, 0x1112, 0x1113}));
1089
1090 //i block write at end
1091 unit.setStartAddress(4);
1092 results.setStartAddress(4);
1093 unit.setValues({0x1, 0x2, 0x3});
1094 QVERIFY(server.setData(unit));
1095 QVERIFY(server.data(&results));
1096 QCOMPARE(results.values(), QVector<quint16>({0x1, 0x2, 0x3}));
1097
1098
1099 unit.setStartAddress(2); // overlap in front
1100 QVERIFY(!server.setData(unit));
1101 unit.setStartAddress(5); // overlap at end
1102 QVERIFY(!server.setData(unit));
1103
1104 data = 0;
1105 QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 7, &data));
1106 QCOMPARE(data, 0);
1107 QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 2, &data));
1108 QCOMPARE(data, 0);
1109
1110 QVERIFY(!server.setData(QModbusDataUnit::HoldingRegisters, 7, 0xabcd));
1111 QVERIFY(!server.setData(QModbusDataUnit::HoldingRegisters, 2, 0xabcd));
1112
1113 QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 7, &data));
1114 QCOMPARE(data, 0);
1115 QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 2, &data));
1116 QCOMPARE(data, 0);
1117 }
1118
1119 void tst_serverAddress()
1120 {
1121 server.setServerAddress(56);
1122 QCOMPARE(server.serverAddress(), 56);
1123 server.setServerAddress(1);
1124 QCOMPARE(server.serverAddress(), 1);
1125 }
1126
1127 void tst_diagnosticRegister()
1128 {
1129 server.setValue(option: QModbusServer::DiagnosticRegister, value: 56u);
1130 QCOMPARE(server.value(QModbusServer::DiagnosticRegister).value<quint16>(), quint16(56));
1131 server.setValue(option: QModbusServer::DiagnosticRegister, value: 1u);
1132 QCOMPARE(server.value(QModbusServer::DiagnosticRegister).value<quint16>(), quint16(1));
1133 }
1134
1135 void tst_exceptionStatusOffset()
1136 {
1137 server.setValue(option: QModbusServer::ExceptionStatusOffset, value: 256u);
1138 QCOMPARE(server.value(QModbusServer::ExceptionStatusOffset).value<quint16>(), quint16(256));
1139 server.setValue(option: QModbusServer::ExceptionStatusOffset, value: 0x0000);
1140 QCOMPARE(server.value(QModbusServer::ExceptionStatusOffset).value<quint16>(), quint16(0));
1141 }
1142
1143 void tst_readWriteDataInheritance()
1144 {
1145 class DebugHandler
1146 {
1147 public:
1148 DebugHandler(QtMessageHandler newMessageHandler)
1149 : oldMessageHandler(qInstallMessageHandler(newMessageHandler)) {}
1150 ~DebugHandler() {
1151 qInstallMessageHandler(oldMessageHandler);
1152 }
1153 private:
1154 QtMessageHandler oldMessageHandler;
1155 };
1156
1157 class InheritanceTestServer : public QModbusServer
1158 {
1159 public:
1160 void close() override {}
1161 bool open() override { return true; }
1162
1163 bool readData(QModbusDataUnit *) const override {
1164 qDebug() << "QModbusServer::data() call did end in the expected OVERRIDE.";
1165 return false;
1166 }
1167 bool writeData(const QModbusDataUnit &) override {
1168 qDebug() << "QModbusServer::setData() call did end in the expected OVERRIDE.";
1169 return false;
1170 }
1171 };
1172
1173 InheritanceTestServer s;
1174 DebugHandler mhs(myMessageHandler);
1175 {
1176 QModbusDataUnit unit;
1177 s.data(newData: &unit);
1178 }
1179 QCOMPARE(s_msg, QString("QModbusServer::data() call did end in the expected OVERRIDE."));
1180 {
1181 s.data(table: QModbusDataUnit::Coils, address: 0u, data: nullptr);
1182 }
1183 QCOMPARE(s_msg, QString("QModbusServer::data() call did end in the expected OVERRIDE."));
1184 {
1185 s.setData(QModbusDataUnit());
1186 }
1187 QCOMPARE(s_msg, QString("QModbusServer::setData() call did end in the expected OVERRIDE."));
1188 {
1189 s.setData(table: QModbusDataUnit::Coils, address: 0u, data: 0u);
1190 }
1191 QCOMPARE(s_msg, QString("QModbusServer::setData() call did end in the expected OVERRIDE."));
1192 }
1193
1194 void testReadWriteDataMissingOrInvalidRegister()
1195 {
1196 TestServer local;
1197 local.setMap({ { QModbusDataUnit::Invalid, QModbusDataUnit() },
1198 { QModbusDataUnit::Coils, QModbusDataUnit(QModbusDataUnit::Coils) },
1199 { QModbusDataUnit::DiscreteInputs, QModbusDataUnit(QModbusDataUnit::DiscreteInputs) }});
1200
1201 QModbusDataUnit invalid;
1202 QCOMPARE(local.data(&invalid), false);
1203 QCOMPARE(local.setData(invalid), false);
1204
1205 QModbusDataUnit missing(QModbusDataUnit::HoldingRegisters);
1206 QCOMPARE(local.data(&missing), false);
1207 QCOMPARE(local.setData(missing), false);
1208 }
1209
1210 void testIllegalTcpFunctionCodes()
1211 {
1212 class ModbusTcpServer : public QModbusTcpServer
1213 {
1214 public:
1215 QModbusResponse processRequest(const QModbusPdu &request) override {
1216 return QModbusTcpServer::processRequest(request);
1217 }
1218
1219 };
1220 ModbusTcpServer local;
1221
1222 QModbusRequest request(QModbusRequest::ReadExceptionStatus);
1223 QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
1224
1225 request = QModbusRequest(QModbusRequest::Diagnostics);
1226 QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
1227
1228 request = QModbusRequest(QModbusRequest::GetCommEventCounter);
1229 QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
1230
1231 request = QModbusRequest(QModbusRequest::GetCommEventLog);
1232 QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
1233
1234 request = QModbusRequest(QModbusRequest::ReportServerId);
1235 QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
1236 }
1237
1238 void testQModbusServerOptions()
1239 {
1240 // TODO: Add a local class implementation to test value()/setValue with a different backing
1241 // store. That's not only related to AsciiInputDelimiter, rather to all enum values there.
1242
1243 QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('\n'));
1244 QCOMPARE(server.setValue(QModbusServer::AsciiInputDelimiter, "Test"), false);
1245 QCOMPARE(server.setValue(QModbusServer::AsciiInputDelimiter, '@'), true);
1246 QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('@'));
1247
1248 QVERIFY(server.setValue(QModbusServer::AsciiInputDelimiter, 'j'));
1249 QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
1250 QVERIFY(server.setValue(QModbusServer::AsciiInputDelimiter, 0x6a));
1251 QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
1252 QVERIFY(server.setValue(QModbusServer::AsciiInputDelimiter, 0x6a));
1253 QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
1254 QVERIFY(!server.setValue(QModbusServer::AsciiInputDelimiter, 0x100));
1255 QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
1256 QVERIFY(!server.setValue(QModbusServer::AsciiInputDelimiter, -1));
1257
1258 TestServer local;
1259 QCOMPARE(local.value(QModbusServer::ListenOnlyMode).toBool(), false);
1260 QCOMPARE(local.setValue(QModbusServer::ListenOnlyMode, "Test"), false);
1261 QCOMPARE(local.setValue(QModbusServer::ListenOnlyMode, true), true);
1262 QCOMPARE(local.value(QModbusServer::ListenOnlyMode).toBool(), true);
1263 }
1264
1265 void testClearOverrunCounterAndFlag()
1266 {
1267 TestServer server;
1268 server.setValue(option: QModbusServer::DiagnosticRegister, value: 0xffff);
1269 server.processRequest(request: QModbusRequest(QModbusRequest::Diagnostics, quint16(0x0014), quint16(0)));
1270 QCOMPARE(server.value(QModbusServer::DiagnosticRegister).value<quint16>(), quint16(0xfffe));
1271 }
1272
1273 void testProcessEncapsulatedInterfaceTransportRequest()
1274 {
1275 QModbusDeviceIdentification objectPool;
1276 QCOMPARE(objectPool.insert(QModbusDeviceIdentification::VendorNameObjectId,
1277 "Company identification"), true);
1278 QCOMPARE(objectPool.isValid(), false);
1279 QCOMPARE(objectPool.insert(QModbusDeviceIdentification::ProductCodeObjectId,
1280 "Product code"), true);
1281 QCOMPARE(objectPool.isValid(), false);
1282 QCOMPARE(objectPool.insert(QModbusDeviceIdentification::MajorMinorRevisionObjectId,
1283 "V2.11"), true);
1284 QCOMPARE(objectPool.isValid(), true);
1285
1286 QCOMPARE(server.setValue(QModbusServer::DeviceIdentification,
1287 QVariant::fromValue<QModbusDeviceIdentification>(objectPool)), true);
1288
1289 auto response = server
1290 .processRequest(request: QModbusRequest(QModbusRequest::EncapsulatedInterfaceTransport,
1291 QByteArray::fromHex(hexEncoded: "0e0100")));
1292 QCOMPARE(response.data(), QByteArray::fromHex("0e01010000030016")
1293 + "Company identification" + QByteArray::fromHex("010c") + "Product code"
1294 + QByteArray::fromHex("0205") + "V2.11");
1295
1296 QCOMPARE(objectPool.insert(QModbusDeviceIdentification::ProductCodeObjectId, QByteArray(
1297 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
1298 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
1299 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")), true);
1300 QCOMPARE(objectPool.isValid(), true);
1301
1302 QCOMPARE(server.setValue(QModbusServer::DeviceIdentification,
1303 QVariant::fromValue<QModbusDeviceIdentification>(objectPool)), true);
1304
1305 response = server.processRequest(request: QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport,
1306 QByteArray::fromHex(hexEncoded: "0e0100")));
1307 QCOMPARE(response.data(), QByteArray::fromHex("0e0101ff02020016")
1308 + "Company identification" + QByteArray::fromHex("01dc") + QByteArray(
1309 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
1310 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
1311 "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
1312
1313 response = server.processRequest(request: QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport,
1314 QByteArray::fromHex(hexEncoded: "0e0102")));
1315 QCOMPARE(response.data(), QByteArray::fromHex("0e01010000010205") + "V2.11");
1316 }
1317};
1318
1319QTEST_MAIN(tst_QModbusServer)
1320
1321#include "tst_qmodbusserver.moc"
1322

source code of qtserialbus/tests/auto/qmodbusserver/tst_qmodbusserver.cpp