blob: ad33337fed48f2a29d289a6dfbbd623f7a5f09f2 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: http://www.qt.io/licensing/
**
** This file is part of the QtSerialBus module of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL3$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see http://www.qt.io/terms-conditions. For further
** information use the contact form at http://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPLv3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or later as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information to
** ensure the GNU General Public License version 2.0 requirements will be
** met: http://www.gnu.org/licenses/gpl-2.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include <QtSerialBus/qmodbusserver.h>
#if QT_CONFIG(modbus_serialport)
#include <QtSerialBus/qmodbusrtuserialslave.h>
#endif
#include <QtSerialBus/qmodbustcpserver.h>
#include <QtSerialBus/qmodbusdeviceidentification.h>
#include <QtCore/qdebug.h>
#include <QtTest/QtTest>
class TestServer : public QModbusServer
{
public:
TestServer() {
qRegisterMetaType<QModbusDataUnit::RegisterType>();
}
bool open() override {
setState(QModbusDevice::ConnectedState);
return true;
}
void close() override {
setState(QModbusDevice::UnconnectedState);
}
QModbusResponse processRequest(const QModbusPdu &request) override
{
return QModbusServer::processRequest(request);
}
};
#define MAP_RANGE 500
static QString s_msg;
static void myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
{
s_msg = msg;
}
class tst_QModbusServer : public QObject
{
Q_OBJECT
private:
TestServer server;
private slots:
void init()
{
QModbusDataUnitMap map;
map.insert(QModbusDataUnit::DiscreteInputs, { QModbusDataUnit::DiscreteInputs, 0, MAP_RANGE });
map.insert(QModbusDataUnit::Coils, { QModbusDataUnit::Coils, 0, MAP_RANGE });
map.insert(QModbusDataUnit::InputRegisters, { QModbusDataUnit::InputRegisters, 0, MAP_RANGE });
map.insert(QModbusDataUnit::HoldingRegisters, { QModbusDataUnit::HoldingRegisters, 0, MAP_RANGE });
server.setMap(map);
}
void testServerAddress()
{
QCOMPARE(QModbusTcpServer().serverAddress(), 0xff);
#if QT_CONFIG(modbus_serialport)
QCOMPARE(QModbusRtuSerialSlave().serverAddress(), 1);
#endif
}
void testProcessRequestReadWriteSingleMultipleCoils()
{
// request write Coil 173, address: 0x00ac -> 172, value: 0xff00 -> ON
QModbusRequest request = QModbusRequest(QModbusRequest::WriteSingleCoil,
QByteArray::fromHex("00acff00"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00acff00"));
// request read Coil 173, address: 0x00ac -> 172, count: 0x0001 -> 1
request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex("00ac0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x01 -> 1, status: 0x01 -> 0000 0001
QCOMPARE(response.data(), QByteArray::fromHex("0101"));
// request write 10 coils starting at coil 20, address: 0x0013 -> 19, count: 0x000a -> 10,
// payload bytes: 0x02 -> 2, values: 0xcd -> 1100 1101, 0x01 -> 0000 0001
request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
QByteArray::fromHex("0013000a02cd01"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request's first 4 bytes
QCOMPARE(response.data(), QByteArray::fromHex("0013000a"));
// request read 10 coils starting at coil 20, address: 0x0013 -> 19, count: 0x000a -> 10
request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex("0013000a"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x02 -> 1, status: 0xcd -> 1100 1101, 0x01 -> 0000 0001
QCOMPARE(response.data(), QByteArray::fromHex("02cd01"));
// request write 19 coils starting at coil 20, address: 0x0013 -> 19, count: 0x0013 -> 19,
// payload bytes: 0x03 -> 3, values: 0xcd -> 1100 1101, 0x6b -> 0110 1011, 0x05 -> 0000 0101
request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
QByteArray::fromHex("0013001303cd6b05"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request's first 4 bytes
QCOMPARE(response.data(), QByteArray::fromHex("00130013"));
// request read 19 coils starting at coil 20, address: 0x0013 -> 19, count: 0x0013 -> 19
request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex("00130013"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x03 -> 3
// status: 0xcd -> 1100 1101, 0x6b -> 0110 1011, 0x05 -> 0000 0101
QCOMPARE(response.data(), QByteArray::fromHex("03cd6b05"));
// request write 10 coils, starting at coil 0, address: 0x0000 -> 0, count: 0x000a -> 10
// payload bytes: 0x02 -> 2, values: 0xcd -> 1100 1101, 0x02 -> 0000 0010
request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
QByteArray::fromHex("0000000a02cd02"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request's first 4 bytes
QCOMPARE(response.data(), QByteArray::fromHex("0000000a"));
// request read 10 coils starting at coil 0, address: 0x0000 -> 0, count: 0x000a -> 10
request = QModbusRequest(QModbusRequest::ReadCoils, QByteArray::fromHex("0000000a"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x02 -> 2, status: 0xcd -> 1100 1101, 0x02 -> 0000 0020
QCOMPARE(response.data(), QByteArray::fromHex("02cd02"));
}
void testProcessReadDiscreteInputsRequest()
{
server.setData(QModbusDataUnit::DiscreteInputs, 172, true);
// request read DiscreteInput 173, address: 0x00ac -> 172, count: 0x0001 -> 1
QModbusRequest request = QModbusRequest(QModbusRequest::ReadDiscreteInputs,
QByteArray::fromHex("00ac0001"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x01 -> 1, status: 0x01 -> 0000 0001
QCOMPARE(response.data(), QByteArray::fromHex("0101"));
server.setData(QModbusDataUnit::DiscreteInputs, 19, quint16(0x0010));
// request read 10 inputs starting at input 20, address: 0x0013 -> 19, count: 0x000a -> 10
request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex("0013000a"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x02 -> 1, status: 0x00 -> 0000 0000, 0x10 -> 0001 0000
QCOMPARE(response.data(), QByteArray::fromHex("020100"));
// request read 10 inputs starting at input 501
request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex("01f5000a"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// request read 2001 inputs starting at input 0
request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex("000007d1"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// request read 0 inputs starting at input 0
request = QModbusRequest(QModbusRequest::ReadDiscreteInputs, QByteArray::fromHex("00000000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessReadHoldingRegistersRequest()
{
server.setData(QModbusDataUnit::HoldingRegisters, 172, 1234u);
server.setData(QModbusDataUnit::HoldingRegisters, 173, 4321u);
// request read holding registers 173, address: 0x00ac -> 172, count: 0x0001 -> 1
QModbusRequest request = QModbusRequest(QModbusRequest::ReadHoldingRegisters,
QByteArray::fromHex("00ac0001"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x02 -> 2, value: 1234u -> 04d2
QCOMPARE(response.data(), QByteArray::fromHex("0204d2"));
// request read 2 registers starting at 172
request = QModbusRequest(QModbusRequest::ReadHoldingRegisters,
QByteArray::fromHex("00ac0002"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x04 -> 4, status: 1234u = 04d2, 4321u =10e1
QCOMPARE(response.data(), QByteArray::fromHex("0404d210e1"));
// request read 10 registers starting at offset 501
request = QModbusRequest(QModbusRequest::ReadHoldingRegisters,
QByteArray::fromHex("01f5000a"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// request read 126 registers starting at offset 0
request = QModbusRequest(QModbusRequest::ReadHoldingRegisters, QByteArray::fromHex("0000007e"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// request read 0 registers starting at offset 0
request = QModbusRequest(QModbusRequest::ReadHoldingRegisters, QByteArray::fromHex("00000000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessReadInputRegistersRequest()
{
server.setData(QModbusDataUnit::InputRegisters, 172, 1234u);
server.setData(QModbusDataUnit::InputRegisters, 173, 4321u);
// request read input registers 173, address: 0x00ac -> 172, count: 0x0001 -> 1
QModbusRequest request = QModbusRequest(QModbusRequest::ReadInputRegisters,
QByteArray::fromHex("00ac0001"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x02 -> 2, value: 1234u -> 04d2
QCOMPARE(response.data(), QByteArray::fromHex("0204d2"));
// request read 2 registers starting at 172
request = QModbusRequest(QModbusRequest::ReadInputRegisters,
QByteArray::fromHex("00ac0002"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, byte count: 0x04 -> 4, status: 1234u = 04d2, 4321u =10e1
QCOMPARE(response.data(), QByteArray::fromHex("0404d210e1"));
// request read 10 registers starting at offset 501
request = QModbusRequest(QModbusRequest::ReadInputRegisters,
QByteArray::fromHex("01f5000a"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// request read 1 register at offset 0 with corrupt message (+1 byte)
request = QModbusRequest(QModbusRequest::ReadInputRegisters,
QByteArray::fromHex("0000000100"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// request read 126 registers starting at offset 0
request = QModbusRequest(QModbusRequest::ReadInputRegisters, QByteArray::fromHex("0000007e"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// request read 0 registers starting at offset 0
request = QModbusRequest(QModbusRequest::ReadInputRegisters, QByteArray::fromHex("00000000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessWriteSingleRegisterRequest()
{
// request write register 173, address: 0x00ac -> 172, value: 0x00ff
QModbusRequest request = QModbusRequest(QModbusRequest::WriteSingleRegister,
QByteArray::fromHex("00ac00ff"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00ac00ff"));
// request write register at offset 501
request = QModbusRequest(QModbusRequest::WriteSingleRegister,
QByteArray::fromHex("01f500ff"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// request write register at offset 0 value only 1 byte
request = QModbusRequest(QModbusRequest::WriteSingleRegister,
QByteArray::fromHex("00007d"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessReadExceptionStatus()
{
// simulate Modicon 484 (start at coil 257 up to 264)
server.setValue(QModbusServer::ExceptionStatusOffset, 256u);
// set the exception status byte to 0000 0011 simulating two bits set
// request write Coil 257, address: 0x0100 -> 256, value: 0xff00 -> ON
QModbusRequest request = QModbusRequest(QModbusRequest::WriteSingleCoil,
QByteArray::fromHex("0100ff00"));
QModbusResponse response = server.processRequest(request);
// request write Coil 258, address: 0x0101 -> 257, value: 0xff00 -> ON
request = QModbusRequest(QModbusRequest::WriteSingleCoil,
QByteArray::fromHex("0101ff00"));
response = server.processRequest(request);
request = QModbusRequest(QModbusRequest::ReadExceptionStatus);
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// invalid request test
request = QModbusRequest(QModbusRequest::ReadExceptionStatus,
QByteArray::fromHex("007d"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessDiagnosticsRequest()
{
// subfunction 00
QModbusRequest request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000000ffabcd"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000000ffabcd"));
//subfunction 01
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00010000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00010000"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0001ff00"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("0001ff00"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0001ff01"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 02
// validate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00020000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00020000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00020001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0002000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 03
// validate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00030a00"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00030a00"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00030a01"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00030a0101"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 04
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00040000"));
response = server.processRequest(request);
QCOMPARE(response.isValid(), false);
QCOMPARE(response.isException(), false);
QCOMPARE(response.data(), QByteArray());
QCOMPARE(response.functionCode(), QModbusResponse::Invalid);
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00040001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0004000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 10: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000a0000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000a0000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000a0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000a000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 11: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000b0000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000b0000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000b0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000b000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 12: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000c0000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000c0000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000c0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000c000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 13: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000d0000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000d0000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000d0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000d000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 14: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000e0000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000e0000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000e0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000e000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 15: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000f0000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("000f0000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000f0001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("000f000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 16: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00100000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00100000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00100001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0010000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 17: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00110000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00110000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00110001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0011000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction 18: counter value is 0
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00120000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00120000"));
// invalidate
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00120001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0012000001"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// subfunction > 4 < 10
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("0005ff01"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("01"));
// subfunction > 18
request = QModbusRequest(QModbusRequest::Diagnostics,
QByteArray::fromHex("00130000"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("01"));
}
void testProcessGetCommEventCounter()
{
TestServer local; // Used later to control the correct event amount.
QModbusRequest request = QModbusRequest(QModbusRequest::GetCommEventCounter);
QModbusResponse response = local.processRequest(request);
QCOMPARE(response.isException(), false);
QCOMPARE(response.data(), QByteArray::fromHex("00000000"));
// TODO: Add more tests once event handling is implemented.
request = QModbusRequest(QModbusRequest::GetCommEventCounter, quint8(10));
response = local.processRequest(request);
QCOMPARE(response.isException(), true);
}
void testProcessGetCommEventLogRequest()
{
TestServer local; // Used later to control the correct event amount.
QModbusRequest request = QModbusRequest(QModbusRequest::GetCommEventLog);
QModbusResponse response = local.processRequest(request);
QCOMPARE(response.isException(), false);
QCOMPARE(response.data(), QByteArray::fromHex("06000000000000"));
// TODO: Add more tests once event handling is implemented.
request = QModbusRequest(QModbusRequest::GetCommEventLog, quint8(10));
response = local.processRequest(request);
QCOMPARE(response.isException(), true);
}
void testProcessWriteMultipleRegistersRequest()
{
// request write at register 173, address: 0x00ac -> 172, value: 0x00ff 0x1234
QModbusRequest request = QModbusRequest(QModbusRequest::WriteMultipleRegisters,
QByteArray::fromHex("00ac00020400ff1234"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00ac0002"));
// request write register at offset 501
request = QModbusRequest(QModbusRequest::WriteMultipleRegisters,
QByteArray::fromHex("01f500010200ff"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// request write 1 register at offset 0 value only 1 byte
request = QModbusRequest(QModbusRequest::WriteMultipleRegisters,
QByteArray::fromHex("000000010200"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testReportServerId()
{
QModbusRequest request = QModbusRequest(QModbusRequest::ReportServerId);
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
QCOMPARE(response.functionCode(), QModbusRequest::ReportServerId);
const QByteArray additionalData = "Qt Modbus Server";
QCOMPARE(server.value(QModbusServer::ServerIdentifier).value<quint8>(), quint8(0x0a));
QCOMPARE(server.value(QModbusServer::RunIndicatorStatus).value<quint8>(), quint8(0xff));
QCOMPARE(server.value(QModbusServer::AdditionalData).toByteArray(), additionalData);
QByteArray data = QByteArray::fromHex("0aff") + additionalData;
data.prepend(quint8(data.size()));
QCOMPARE(response.data(), data);
request = QModbusRequest(QModbusRequest::ReportServerId, data);
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.exceptionCode(), QModbusPdu::IllegalDataValue);
server.setValue(QModbusServer::ServerIdentifier, quint8(0xff));
QCOMPARE(server.setValue(QModbusServer::ServerIdentifier, additionalData), false);
QCOMPARE(server.value(QModbusServer::ServerIdentifier).value<quint8>(), quint8(0xff));
server.setValue(QModbusServer::RunIndicatorStatus, quint8(0x00));
QCOMPARE(server.setValue(QModbusServer::RunIndicatorStatus, quint8(0xab)), false);
QCOMPARE(server.value(QModbusServer::RunIndicatorStatus).value<quint8>(), quint8(0x00));
server.setValue(QModbusServer::AdditionalData, QByteArray("TestData"));
QCOMPARE(server.setValue(QModbusServer::AdditionalData, QStringList()), false);
QCOMPARE(server.value(QModbusServer::AdditionalData).toByteArray(), QByteArray("TestData"));
}
void testMaskWriteRegister()
{
// preset register 172 with value 18 (0x0012h)
server.setData(QModbusDataUnit::HoldingRegisters, 172, 18u);
//mask request register 172 with andMask: 242 (0x00f2)
// orMask: 37 (0x0025)
// result: 23 (0x0017)
QModbusRequest request = QModbusRequest(QModbusRequest::MaskWriteRegister,
QByteArray::fromHex("00ac00f20025"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("00ac00f20025"));
// validate contents after masking
quint16 data;
QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 172, &data));
QCOMPARE(data, quint16(23));
// invalidate use register 501:
request = QModbusRequest(QModbusRequest::MaskWriteRegister,
QByteArray::fromHex("01f500f20025"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// invalidate with one bytes less data:
request = QModbusRequest(QModbusRequest::MaskWriteRegister,
QByteArray::fromHex("00ac00f200"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// invalidate with one bytes more data:
request = QModbusRequest(QModbusRequest::MaskWriteRegister,
QByteArray::fromHex("00ac00f2002500"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessReadWriteMultipleRegistersRequest()
{
server.setData(QModbusDataUnit::HoldingRegisters, 172, 1234u);
server.setData(QModbusDataUnit::HoldingRegisters, 173, 1235u);
server.setData(QModbusDataUnit::HoldingRegisters, 174, 1236u);
// request read 3 registers at register 173, address: 0x00ac -> 172,
// write 3 registers at register 173: 1237u, 1238u, 1239u
QModbusRequest request = QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters,
QByteArray::fromHex("00ac000300ac00030604d504d604d7"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
// response, equals request
QCOMPARE(response.data(), QByteArray::fromHex("0604d504d604d7"));
// request write register at offset 501
request = QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters,
QByteArray::fromHex("00ac000301f500010200ff"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// request write 3 registers at offset 173 value only 5 bytes
request = QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters,
QByteArray::fromHex("00ac000300ac00030604d504d604"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
}
void testProcessReadFifoQueue()
{
// prepare a fifo with two object values, pointer address is 172 with value 2 items
server.setData(QModbusDataUnit::HoldingRegisters, 172, 2u);
server.setData(QModbusDataUnit::HoldingRegisters, 173, 1235u);
server.setData(QModbusDataUnit::HoldingRegisters, 174, 1236u);
// request read fifo queue at fifo pointer address 172
QModbusRequest request = QModbusRequest(QModbusRequest::ReadFifoQueue,
QByteArray::fromHex("00ac"));
QModbusResponse response = server.processRequest(request);
QCOMPARE(response.isException(), false);
QCOMPARE(response.data(), QByteArray::fromHex("0006000204d304d4"));
// invalidate tests
// invalid offset address (501)
request = QModbusRequest(QModbusRequest::ReadFifoQueue,
QByteArray::fromHex("01f5"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
// invalid fifo count > 31
server.setData(QModbusDataUnit::HoldingRegisters, 172, 32u);
request = QModbusRequest(QModbusRequest::ReadFifoQueue,
QByteArray::fromHex("00ac"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("03"));
// invalid fifo data address beyond 500, fifo values 3 (500-502)
server.setData(QModbusDataUnit::HoldingRegisters, 499, 3u);
request = QModbusRequest(QModbusRequest::ReadFifoQueue,
QByteArray::fromHex("01f3"));
response = server.processRequest(request);
QCOMPARE(response.isException(), true);
QCOMPARE(response.data(), QByteArray::fromHex("02"));
}
void tst_dataCalls_data()
{
QTest::addColumn<QModbusDataUnit::RegisterType>("registerType");
QTest::newRow("InitCoils") << QModbusDataUnit::Coils;
QTest::newRow("InitDiscreteInputs") << QModbusDataUnit::DiscreteInputs;
QTest::newRow("InitInputRegisters") << QModbusDataUnit::InputRegisters;
QTest::newRow("InitHoldingRegisters") << QModbusDataUnit::HoldingRegisters;
QTest::newRow("InitInvalid") << QModbusDataUnit::Invalid;
}
void tst_dataCalls()
{
QFETCH(QModbusDataUnit::RegisterType, registerType);
// basic assumption, all registers have start address 0 and size 500
const bool validDataUnit = (registerType != QModbusDataUnit::Invalid);
//test initialization of 0
if (validDataUnit) {
for (int i = 0; i < MAP_RANGE; i++) {
quint16 data = 123;
QVERIFY(server.data(registerType, i, &data));
QCOMPARE(data, quint16(0));
}
} else {
quint16 data = 0;
QVERIFY(!server.data(registerType, 0 , &data));
}
quint16 data = 0;
QSignalSpy writtenSpy(
&server, SIGNAL(dataWritten(QModbusDataUnit::RegisterType,int,int)));
QVERIFY(writtenSpy.isEmpty());
QVERIFY(!server.data(registerType, MAP_RANGE+1, &data)); // out of range
QCOMPARE(data, quint16(0));
QVERIFY(!server.data(registerType, 1, 0)); // invalid data pointer
QCOMPARE(data, quint16(0));
QCOMPARE(server.setData(registerType, 1, 444), validDataUnit);
QCOMPARE(server.data(registerType, 1, &data), validDataUnit);
if (validDataUnit) {
QCOMPARE(data, quint16(444));
QTRY_COMPARE(writtenSpy.count(), 1);
QList<QVariant> signalData = writtenSpy.at(0);
QCOMPARE(signalData.count(), 3);
QCOMPARE(signalData.at(0).value<QModbusDataUnit::RegisterType>(), registerType);
QCOMPARE(signalData.at(1).toInt(), 1);
QCOMPARE(signalData.at(2).toInt(), 1);
} else {
QCOMPARE(data, quint16(0));
QTRY_VERIFY(writtenSpy.isEmpty());
}
//write 444 again but this time no dataWritten since no value change
data = 0;
writtenSpy.clear();
QCOMPARE(server.setData(registerType, 1, 444), validDataUnit);
QCOMPARE(server.data(registerType, 1, &data), validDataUnit);
if (validDataUnit)
QCOMPARE(data, quint16(444));
else
QCOMPARE(data, quint16(0));
QTRY_VERIFY(writtenSpy.isEmpty()); //
QVERIFY(!server.data(registerType, 1, 0)); // out of range although value set
QVERIFY(!server.setData(registerType, -1, 1));
QVERIFY(!server.setData(registerType, MAP_RANGE+1, 1));
QTRY_VERIFY(writtenSpy.isEmpty());
//testing server.setData(ModbusDataUnit&)
const QVector<quint16> valueVector = { 1, 1, 1, 1, 1};
const QVector<quint16> zeroVector = { 0, 0, 0, 0, 0};
QModbusDataUnit rangeUnit(registerType, 7, valueVector);
QCOMPARE(rangeUnit.valueCount(), 5u);
QCOMPARE(rangeUnit.values().count(), 5);
QCOMPARE(rangeUnit.startAddress(), 7);
QVERIFY(rangeUnit.values() == valueVector);
QVERIFY(rangeUnit.registerType() == registerType);
writtenSpy.clear();
QVERIFY(server.setData(rangeUnit) == validDataUnit);
if (validDataUnit) {
for (int i = rangeUnit.startAddress();
i < rangeUnit.startAddress() + int(rangeUnit.valueCount()); i++) {
quint16 readData = 0;
QVERIFY(server.data(registerType, i, &readData));
QCOMPARE(readData, valueVector.at(i-rangeUnit.startAddress()));
}
QTRY_COMPARE(writtenSpy.count(), 1);
QList<QVariant> signalData = writtenSpy.at(0);
QCOMPARE(signalData.count(), 3);
QCOMPARE(signalData.at(0).value<QModbusDataUnit::RegisterType>(), registerType);
QCOMPARE(signalData.at(1).toInt(), rangeUnit.startAddress());
QCOMPARE(signalData.at(2).toUInt(), rangeUnit.valueCount());
}
// no writtenData() signal when writing same rangeUnit again
writtenSpy.clear();
QVERIFY(server.setData(rangeUnit) == validDataUnit);
QTRY_VERIFY(writtenSpy.isEmpty());
//never fits anywhere
QModbusDataUnit oversizeUnit(registerType, 0, MAP_RANGE*2);
QCOMPARE(oversizeUnit.valueCount(), uint(MAP_RANGE*2));
QCOMPARE(oversizeUnit.values().count(), MAP_RANGE*2);
QCOMPARE(oversizeUnit.startAddress(), 0);
QCOMPARE(oversizeUnit.registerType(), registerType);
//completely outside of valid range
writtenSpy.clear();
rangeUnit.setStartAddress(MAP_RANGE + 1 );
QVERIFY(!server.setData(rangeUnit));
//slightly outside of valid range
rangeUnit.setStartAddress(MAP_RANGE - 2);
QVERIFY(!server.setData(rangeUnit));
//slightly outside of valid range in the bottom
rangeUnit.setStartAddress(-1);
QVERIFY(!server.setData(rangeUnit));
//input data unit doesn't fit
QVERIFY(!server.setData(oversizeUnit));
oversizeUnit.setStartAddress(-1);
QVERIFY(!server.setData(oversizeUnit));
oversizeUnit.setStartAddress(MAP_RANGE+1);
QVERIFY(!server.setData(oversizeUnit));
oversizeUnit.setStartAddress(MAP_RANGE-2);
QVERIFY(!server.setData(oversizeUnit));
QTRY_VERIFY(writtenSpy.isEmpty());
//testing server.data(QModbusDataUnit *)
QModbusDataUnit requestUnit(registerType, 7, 5);
QCOMPARE(requestUnit.valueCount(), 5u);
QCOMPARE(requestUnit.values().count(), 5);
QCOMPARE(requestUnit.startAddress(), 7);
QVERIFY(requestUnit.registerType() == registerType);
QVERIFY(requestUnit.values() != valueVector);
QVERIFY(server.data(&requestUnit) == validDataUnit);
if (validDataUnit) {
QVERIFY(requestUnit.values() == valueVector);
QCOMPARE(requestUnit.valueCount(), 5u);
QCOMPARE(requestUnit.values().count(), 5);
QCOMPARE(requestUnit.startAddress(), 7);
}
requestUnit.setValues(zeroVector);
QVERIFY(requestUnit.values() != valueVector);
QVERIFY(requestUnit.values() == zeroVector);
requestUnit.setStartAddress(MAP_RANGE + 1);
QVERIFY(!server.data(&requestUnit));
requestUnit.setStartAddress(MAP_RANGE - 2);
QVERIFY(!server.data(&requestUnit));
// ask for entire map
requestUnit.setStartAddress(-1);
QVERIFY(server.data(&requestUnit) == validDataUnit);
if (validDataUnit) {
QCOMPARE(requestUnit.valueCount(), uint(MAP_RANGE));
QCOMPARE(requestUnit.values().count(), MAP_RANGE);
}
oversizeUnit.setStartAddress(0);
QVERIFY(!server.data(&oversizeUnit));
oversizeUnit.setStartAddress(MAP_RANGE+1);
QVERIFY(!server.data(&oversizeUnit));
oversizeUnit.setStartAddress(MAP_RANGE-2);
QVERIFY(!server.data(&oversizeUnit));
oversizeUnit.setStartAddress(-1);
QVERIFY(server.data(&oversizeUnit) == validDataUnit);
if (validDataUnit) {
QCOMPARE(oversizeUnit.valueCount(), uint(MAP_RANGE));
QCOMPARE(oversizeUnit.values().count(), MAP_RANGE);
}
}
void tst_dataCallsShiftedIndex()
{
TestServer overlapIndex;
const quint16 dataCount = 4;
QModbusDataUnitMap map;
map.insert(QModbusDataUnit::HoldingRegisters, {QModbusDataUnit::HoldingRegisters, 3, dataCount});
server.setMap(map);
QCOMPARE(map.value(QModbusDataUnit::HoldingRegisters).valueCount(), dataCount);
QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 3, 0xaaaa));
QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 4, 0xbbbb));
QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 5, 0xcccc));
QVERIFY(server.setData(QModbusDataUnit::HoldingRegisters, 6, 0xdddd));
// ********** Test individual access ********** //
quint16 data = 0;
QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 3, &data));
QCOMPARE(data, 0xaaaa);
QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 4, &data));
QCOMPARE(data, 0xbbbb);
QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 5, &data));
QCOMPARE(data, 0xcccc);
QVERIFY(server.data(QModbusDataUnit::HoldingRegisters, 6, &data));
QCOMPARE(data, 0xdddd);
// block write at start
QModbusDataUnit unit(QModbusDataUnit::HoldingRegisters, 3, 3);
for (int i = 0; i < 3; i++)
unit.setValue(i, quint16(0x1111 + i));
QVERIFY(server.setData(unit));
QModbusDataUnit results(QModbusDataUnit::HoldingRegisters, 3, 3);
QVERIFY(server.data(&results));
QCOMPARE(results.values(), QVector<quint16>({0x1111, 0x1112, 0x1113}));
//i block write at end
unit.setStartAddress(4);
results.setStartAddress(4);
unit.setValues({0x1, 0x2, 0x3});
QVERIFY(server.setData(unit));
QVERIFY(server.data(&results));
QCOMPARE(results.values(), QVector<quint16>({0x1, 0x2, 0x3}));
unit.setStartAddress(2); // overlap in front
QVERIFY(!server.setData(unit));
unit.setStartAddress(5); // overlap at end
QVERIFY(!server.setData(unit));
data = 0;
QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 7, &data));
QCOMPARE(data, 0);
QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 2, &data));
QCOMPARE(data, 0);
QVERIFY(!server.setData(QModbusDataUnit::HoldingRegisters, 7, 0xabcd));
QVERIFY(!server.setData(QModbusDataUnit::HoldingRegisters, 2, 0xabcd));
QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 7, &data));
QCOMPARE(data, 0);
QVERIFY(!server.data(QModbusDataUnit::HoldingRegisters, 2, &data));
QCOMPARE(data, 0);
}
void tst_serverAddress()
{
server.setServerAddress(56);
QCOMPARE(server.serverAddress(), 56);
server.setServerAddress(1);
QCOMPARE(server.serverAddress(), 1);
}
void tst_diagnosticRegister()
{
server.setValue(QModbusServer::DiagnosticRegister, 56u);
QCOMPARE(server.value(QModbusServer::DiagnosticRegister).value<quint16>(), quint16(56));
server.setValue(QModbusServer::DiagnosticRegister, 1u);
QCOMPARE(server.value(QModbusServer::DiagnosticRegister).value<quint16>(), quint16(1));
}
void tst_exceptionStatusOffset()
{
server.setValue(QModbusServer::ExceptionStatusOffset, 256u);
QCOMPARE(server.value(QModbusServer::ExceptionStatusOffset).value<quint16>(), quint16(256));
server.setValue(QModbusServer::ExceptionStatusOffset, 0x0000);
QCOMPARE(server.value(QModbusServer::ExceptionStatusOffset).value<quint16>(), quint16(0));
}
void tst_readWriteDataInheritance()
{
class DebugHandler
{
public:
DebugHandler(QtMessageHandler newMessageHandler)
: oldMessageHandler(qInstallMessageHandler(newMessageHandler)) {}
~DebugHandler() {
qInstallMessageHandler(oldMessageHandler);
}
private:
QtMessageHandler oldMessageHandler;
};
class InheritanceTestServer : public QModbusServer
{
public:
void close() override {}
bool open() override { return true; }
bool readData(QModbusDataUnit *) const override {
qDebug() << "QModbusServer::data() call did end in the expected OVERRIDE.";
return false;
}
bool writeData(const QModbusDataUnit &) override {
qDebug() << "QModbusServer::setData() call did end in the expected OVERRIDE.";
return false;
}
};
InheritanceTestServer s;
DebugHandler mhs(myMessageHandler);
{
QModbusDataUnit unit;
s.data(&unit);
}
QCOMPARE(s_msg, QString("QModbusServer::data() call did end in the expected OVERRIDE."));
{
s.data(QModbusDataUnit::Coils, 0u, nullptr);
}
QCOMPARE(s_msg, QString("QModbusServer::data() call did end in the expected OVERRIDE."));
{
s.setData(QModbusDataUnit());
}
QCOMPARE(s_msg, QString("QModbusServer::setData() call did end in the expected OVERRIDE."));
{
s.setData(QModbusDataUnit::Coils, 0u, 0u);
}
QCOMPARE(s_msg, QString("QModbusServer::setData() call did end in the expected OVERRIDE."));
}
void testReadWriteDataMissingOrInvalidRegister()
{
TestServer local;
local.setMap({ { QModbusDataUnit::Invalid, QModbusDataUnit() },
{ QModbusDataUnit::Coils, QModbusDataUnit(QModbusDataUnit::Coils) },
{ QModbusDataUnit::DiscreteInputs, QModbusDataUnit(QModbusDataUnit::DiscreteInputs) }});
QModbusDataUnit invalid;
QCOMPARE(local.data(&invalid), false);
QCOMPARE(local.setData(invalid), false);
QModbusDataUnit missing(QModbusDataUnit::HoldingRegisters);
QCOMPARE(local.data(&missing), false);
QCOMPARE(local.setData(missing), false);
}
void testIllegalTcpFunctionCodes()
{
class ModbusTcpServer : public QModbusTcpServer
{
public:
QModbusResponse processRequest(const QModbusPdu &request) override {
return QModbusTcpServer::processRequest(request);
}
};
ModbusTcpServer local;
QModbusRequest request(QModbusRequest::ReadExceptionStatus);
QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
request = QModbusRequest(QModbusRequest::Diagnostics);
QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
request = QModbusRequest(QModbusRequest::GetCommEventCounter);
QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
request = QModbusRequest(QModbusRequest::GetCommEventLog);
QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
request = QModbusRequest(QModbusRequest::ReportServerId);
QCOMPARE(local.processRequest(request).exceptionCode(), QModbusPdu::IllegalFunction);
}
void testQModbusServerOptions()
{
// TODO: Add a local class implementation to test value()/setValue with a different backing
// store. That's not only related to AsciiInputDelimiter, rather to all enum values there.
QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('\n'));
QCOMPARE(server.setValue(QModbusServer::AsciiInputDelimiter, "Test"), false);
QCOMPARE(server.setValue(QModbusServer::AsciiInputDelimiter, '@'), true);
QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('@'));
QVERIFY(server.setValue(QModbusServer::AsciiInputDelimiter, 'j'));
QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
QVERIFY(server.setValue(QModbusServer::AsciiInputDelimiter, 0x6a));
QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
QVERIFY(server.setValue(QModbusServer::AsciiInputDelimiter, 0x6a));
QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
QVERIFY(!server.setValue(QModbusServer::AsciiInputDelimiter, 0x100));
QCOMPARE(server.value(QModbusServer::AsciiInputDelimiter).toInt(), int('j'));
QVERIFY(!server.setValue(QModbusServer::AsciiInputDelimiter, -1));
TestServer local;
QCOMPARE(local.value(QModbusServer::ListenOnlyMode).toBool(), false);
QCOMPARE(local.setValue(QModbusServer::ListenOnlyMode, "Test"), false);
QCOMPARE(local.setValue(QModbusServer::ListenOnlyMode, true), true);
QCOMPARE(local.value(QModbusServer::ListenOnlyMode).toBool(), true);
}
void testClearOverrunCounterAndFlag()
{
TestServer server;
server.setValue(QModbusServer::DiagnosticRegister, 0xffff);
server.processRequest(QModbusRequest(QModbusRequest::Diagnostics, quint16(0x0014), quint16(0)));
QCOMPARE(server.value(QModbusServer::DiagnosticRegister).value<quint16>(), quint16(0xfffe));
}
void testProcessEncapsulatedInterfaceTransportRequest()
{
QModbusDeviceIdentification objectPool;
QCOMPARE(objectPool.insert(QModbusDeviceIdentification::VendorNameObjectId,
"Company identification"), true);
QCOMPARE(objectPool.isValid(), false);
QCOMPARE(objectPool.insert(QModbusDeviceIdentification::ProductCodeObjectId,
"Product code"), true);
QCOMPARE(objectPool.isValid(), false);
QCOMPARE(objectPool.insert(QModbusDeviceIdentification::MajorMinorRevisionObjectId,
"V2.11"), true);
QCOMPARE(objectPool.isValid(), true);
QCOMPARE(server.setValue(QModbusServer::DeviceIdentification,
QVariant::fromValue<QModbusDeviceIdentification>(objectPool)), true);
auto response = server
.processRequest(QModbusRequest(QModbusRequest::EncapsulatedInterfaceTransport,
QByteArray::fromHex("0e0100")));
QCOMPARE(response.data(), QByteArray::fromHex("0e01010000030016")
+ "Company identification" + QByteArray::fromHex("010c") + "Product code"
+ QByteArray::fromHex("0205") + "V2.11");
QCOMPARE(objectPool.insert(QModbusDeviceIdentification::ProductCodeObjectId, QByteArray(
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX")), true);
QCOMPARE(objectPool.isValid(), true);
QCOMPARE(server.setValue(QModbusServer::DeviceIdentification,
QVariant::fromValue<QModbusDeviceIdentification>(objectPool)), true);
response = server.processRequest(QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport,
QByteArray::fromHex("0e0100")));
QCOMPARE(response.data(), QByteArray::fromHex("0e0101ff02020016")
+ "Company identification" + QByteArray::fromHex("01dc") + QByteArray(
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
"XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"));
response = server.processRequest(QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport,
QByteArray::fromHex("0e0102")));
QCOMPARE(response.data(), QByteArray::fromHex("0e01010000010205") + "V2.11");
}
};
QTEST_MAIN(tst_QModbusServer)
#include "tst_qmodbusserver.moc"