Revision control
++ // Copyright (C) 2017 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
++
++ #include <QtCore/qdebug.h>
++ #include <QtSerialBus/qmodbuspdu.h>
++
++ #include <QtTest/QtTest>
++
++ static QString s_msg;
++ static void myMessageHandler(QtMsgType, const QMessageLogContext &, const QString &msg)
++ {
++ s_msg = msg;
++ }
++
++ class DebugHandler
++ {
++ public:
++ DebugHandler(QtMessageHandler newMessageHandler)
++ : oldMessageHandler(qInstallMessageHandler(newMessageHandler)) {}
++ ~DebugHandler() {
++ qInstallMessageHandler(oldMessageHandler);
++ }
++ private:
++ QtMessageHandler oldMessageHandler;
++ };
++
++ static int minimumDataSize(const QModbusPdu &pdu, bool request)
++ {
++ if (pdu.isException())
++ return 1;
++
++ switch (pdu.functionCode()) {
++ case QModbusPdu::ReadCoils:
++ case QModbusPdu::ReadDiscreteInputs:
++ return request ? 4 : 2;
++ case QModbusPdu::WriteSingleCoil:
++ case QModbusPdu::WriteSingleRegister:
++ return 4;
++ case QModbusPdu::ReadHoldingRegisters:
++ case QModbusPdu::ReadInputRegisters:
++ return request ? 4 : 3;
++ case QModbusPdu::ReadExceptionStatus:
++ return request ? 0 : 1;
++ case QModbusPdu::Diagnostics:
++ return 4;
++ case QModbusPdu::GetCommEventCounter:
++ return request ? 0 : 4;
++ case QModbusPdu::GetCommEventLog:
++ return request ? 0 : 8;
++ case QModbusPdu::WriteMultipleCoils:
++ return request ? 6 : 4;
++ case QModbusPdu::WriteMultipleRegisters:
++ return request ? 7 : 4;
++ case QModbusPdu::ReportServerId:
++ return request ? 0 : 3;
++ case QModbusPdu::ReadFileRecord:
++ return request ? 8 : 5;
++ case QModbusPdu::WriteFileRecord:
++ return 10;
++ case QModbusPdu::MaskWriteRegister:
++ return 6;
++ case QModbusPdu::ReadWriteMultipleRegisters:
++ return request ? 11 : 3;
++ case QModbusPdu::ReadFifoQueue:
++ return request ? 2 : 6;
++ case QModbusPdu::EncapsulatedInterfaceTransport:
++ return 2;
++ case QModbusPdu::Invalid:
++ case QModbusPdu::UndefinedFunctionCode:
++ return -1;
++ }
++ return -1;
++ }
++
++ class tst_QModbusPdu : public QObject
++ {
++ Q_OBJECT
++
++ private slots:
++ void testDefaultConstructor()
++ {
++ QModbusRequest request;
++ QCOMPARE(request.isValid(), false);
++ QCOMPARE(request.isException(), false);
++ QCOMPARE(request.functionCode(), QModbusRequest::FunctionCode(0x00));
++ QCOMPARE(request.exceptionCode(), QModbusPdu::ExtendedException);
++ QCOMPARE(request.data().toHex(), QByteArray());
++
++ QModbusResponse response;
++ QCOMPARE(response.isValid(), false);
++ QCOMPARE(response.isException(), false);
++ QCOMPARE(response.functionCode(), QModbusResponse::FunctionCode(0x00));
++ QCOMPARE(response.exceptionCode(), QModbusPdu::ExtendedException);
++ QCOMPARE(response.data().toHex(), QByteArray());
++
++ QModbusExceptionResponse exception;
++ QCOMPARE(exception.isValid(), false);
++ QCOMPARE(exception.isException(), false);
++ QCOMPARE(exception.exceptionCode(), QModbusPdu::ExtendedException);
++ QCOMPARE(exception.functionCode(), QModbusExceptionResponse::FunctionCode(0x00));
++ QCOMPARE(exception.data().toHex(), QByteArray());
++ }
++
++ void testVaridicConstructor()
++ {
++ QModbusRequest request(QModbusRequest::ReportServerId);
++ QCOMPARE(request.isValid(), true);
++ QCOMPARE(request.isException(), false);
++ QCOMPARE(request.functionCode(), QModbusRequest::ReportServerId);
++ QCOMPARE(request.exceptionCode(), QModbusPdu::ExtendedException);
++ QCOMPARE(request.data().toHex(), QByteArray());
++
++ // Byte Count, Server ID, Run Indicator Status
++ QModbusResponse response(QModbusResponse::ReportServerId, quint8(0x02), quint8(0x01),
++ quint8(0xff));
++ QCOMPARE(response.isValid(), true);
++ QCOMPARE(response.isException(), false);
++ QCOMPARE(response.functionCode(), QModbusResponse::ReportServerId);
++ QCOMPARE(response.exceptionCode(), QModbusPdu::ExtendedException);
++ QCOMPARE(response.data().toHex(), QByteArray("0201ff"));
++ }
++
++ void testQByteArrayConstructor()
++ {
++ QModbusRequest request(QModbusRequest::ReportServerId, QByteArray());
++ QCOMPARE(request.isValid(), true);
++ QCOMPARE(request.isException(), false);
++ QCOMPARE(request.functionCode(), QModbusRequest::ReportServerId);
++ QCOMPARE(request.data().toHex(), QByteArray());
++
++ // Byte Count, Server ID, Run Indicator Status
++ QModbusResponse response(QModbusResponse::ReportServerId, QByteArray::fromHex("ff0102"));
++ QCOMPARE(response.isValid(), true);
++ QCOMPARE(response.isException(), false);
++ QCOMPARE(response.functionCode(), QModbusResponse::ReportServerId);
++ QCOMPARE(response.data().toHex(), QByteArray("ff0102"));
++ }
++
++ void testCopyConstructorAndAssignment()
++ {
++ QModbusPdu pdu;
++ pdu.setFunctionCode(QModbusPdu::ReadCoils);
++
++ QModbusRequest request(pdu);
++ QCOMPARE(request.functionCode(), QModbusResponse::ReadCoils);
++
++ QModbusResponse response(pdu);
++ QCOMPARE(response.functionCode(), QModbusResponse::ReadCoils);
++
++ pdu.setFunctionCode(QModbusPdu::WriteMultipleCoils);
++
++ request = pdu;
++ QCOMPARE(request.functionCode(), QModbusResponse::WriteMultipleCoils);
++
++ response = pdu;
++ QCOMPARE(response.functionCode(), QModbusResponse::WriteMultipleCoils);
++ }
++
++ void testEncodeDecode()
++ {
++ QModbusRequest invalid;
++ quint8 data = 0xff;
++ invalid.decodeData(&data);
++ QCOMPARE(data, quint8(0xff));
++ invalid.encodeData(data);
++ QCOMPARE(invalid.data().toHex(), QByteArray("ff"));
++
++ data = 0x00;
++ QModbusRequest request(QModbusRequest::ReportServerId);
++ request.decodeData(&data);
++ QCOMPARE(data, quint8(0x00));
++ QCOMPARE(request.data().toHex(), QByteArray());
++
++ QModbusResponse response(QModbusResponse::ReportServerId);
++ response.encodeData(quint8(0x02), quint8(0x01), quint8(0xff));
++ quint8 count = 0, id = 0, run = 0;
++ response.decodeData(&count, &id, &run);
++ QCOMPARE(count, quint8(0x02));
++ QCOMPARE(id, quint8(0x01));
++ QCOMPARE(run, quint8(0xff));
++ QCOMPARE(response.data().toHex(), QByteArray("0201ff"));
++
++ count = 0, id = 0, run = 0;
++ response = QModbusResponse(QModbusResponse::ReportServerId, QByteArray::fromHex("0201ff"));
++ QCOMPARE(response.isValid(), true);
++ QCOMPARE(response.isException(), false);
++ QCOMPARE(response.functionCode(), QModbusResponse::ReportServerId);
++ QCOMPARE(response.data().toHex(), QByteArray("0201ff"));
++ response.decodeData(&count, &id, &run);
++ QCOMPARE(count, quint8(0x02));
++ QCOMPARE(id, quint8(0x01));
++ QCOMPARE(run, quint8(0xff));
++
++ QModbusRequest readCoils(QModbusRequest::ReadCoils);
++ // starting address and quantity of coils
++ readCoils.encodeData(quint16(12), quint16(10));
++ quint16 address, quantity;
++ readCoils.decodeData(&address, &quantity);
++ QCOMPARE(address, quint16(0x0c));
++ QCOMPARE(quantity, quint16(0x0a));
++
++ request = QModbusRequest(QModbusRequest::WriteMultipleCoils,
++ QByteArray::fromHex("0013000a02cd01"));
++ QCOMPARE(request.data().toHex(), QByteArray("0013000a02cd01"));
++ address = 0xffff, quantity = 0xffff;
++ quint8 bytes = 0xff, firstByte = 0xff, secondByte = 0xff;
++ request.decodeData(&address, &quantity, &bytes, &firstByte, &secondByte);
++ QCOMPARE(address, quint16(19));
++ QCOMPARE(quantity, quint16(10));
++ QCOMPARE(bytes, quint8(2));
++ QCOMPARE(firstByte, quint8(0xcd));
++ QCOMPARE(secondByte, quint8(0x01));
++ }
++
++ void testQModbusExceptionResponsePdu()
++ {
++ QModbusExceptionResponse exception(QModbusExceptionResponse::ReportServerId,
++ QModbusExceptionResponse::ServerDeviceFailure);
++ QCOMPARE(exception.isValid(), true);
++ QCOMPARE(exception.isException(), true);
++ QCOMPARE(exception.functionCode(), QModbusExceptionResponse::ReportServerId);
++ QCOMPARE(exception.data().toHex(), QByteArray("04"));
++ QCOMPARE(exception.exceptionCode(), QModbusExceptionResponse::ServerDeviceFailure);
++
++ QModbusExceptionResponse response;
++ QCOMPARE(response.isValid(), false);
++ QCOMPARE(response.isException(), false);
++ QCOMPARE(response.functionCode(), QModbusExceptionResponse::Invalid);
++
++ response.setFunctionCode(QModbusExceptionResponse::ReportServerId);
++ response.setExceptionCode(QModbusExceptionResponse::ServerDeviceFailure);
++ QCOMPARE(response.isValid(), true);
++ QCOMPARE(response.isException(), true);
++ QCOMPARE(response.functionCode(), QModbusExceptionResponse::ReportServerId);
++ QCOMPARE(response.data().toHex(), QByteArray("04"));
++ QCOMPARE(exception.exceptionCode(), QModbusExceptionResponse::ServerDeviceFailure);
++
++ QModbusPdu pdu;
++ pdu.setFunctionCode(QModbusExceptionResponse::FunctionCode(QModbusPdu::ReadCoils
++ | QModbusPdu::ExceptionByte));
++ QCOMPARE(pdu.isException(), true);
++ QCOMPARE(pdu.functionCode(), QModbusPdu::ReadCoils);
++
++ QModbusExceptionResponse exception2(pdu);
++ QCOMPARE(exception2.isException(), true);
++ QCOMPARE(exception2.functionCode(), QModbusPdu::ReadCoils);
++ QCOMPARE(exception2.exceptionCode(), QModbusExceptionResponse::ExtendedException);
++ exception2.setExceptionCode(QModbusExceptionResponse::IllegalFunction);
++ QCOMPARE(exception2.exceptionCode(), QModbusExceptionResponse::IllegalFunction);
++
++ QModbusExceptionResponse exception3 = pdu;
++ QCOMPARE(exception3.isException(), true);
++ QCOMPARE(exception3.functionCode(), QModbusPdu::ReadCoils);
++ QCOMPARE(exception3.exceptionCode(), QModbusExceptionResponse::ExtendedException);
++ }
++
++ void testQDebugStreamOperator()
++ {
++ DebugHandler mhs(myMessageHandler);
++ {
++ QDebug d = qDebug();
++ d << QModbusPdu();
++ d << QModbusRequest();
++ d << QModbusResponse();
++ d << QModbusExceptionResponse();
++ }
++ QCOMPARE(s_msg, QString::fromLatin1("0x00 0x00 0x00 0x00"));
++
++ {
++ QDebug d = qDebug();
++ d << QModbusRequest(QModbusRequest::ReadCoils, quint16(19), quint16(19));
++ }
++ QCOMPARE(s_msg, QString::fromLatin1("0x0100130013"));
++
++ {
++ QDebug d = qDebug();
++ d << QModbusResponse(QModbusResponse::ReadCoils, QByteArray::fromHex("03cd6b05"));
++ }
++ QCOMPARE(s_msg, QString::fromLatin1("0x0103cd6b05"));
++
++ {
++ QDebug d = qDebug();
++ d << QModbusExceptionResponse(QModbusExceptionResponse::ReadCoils,
++ QModbusExceptionResponse::IllegalDataAddress);
++ }
++ QCOMPARE(s_msg, QString::fromLatin1("0x8102"));
++ }
++
++ void testMinimumDataSize()
++ {
++ bool request = true;
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadCoils), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadCoils)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadDiscreteInputs), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadDiscreteInputs)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteSingleCoil), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::WriteSingleCoil)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadHoldingRegisters), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadHoldingRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadInputRegisters), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadInputRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteSingleRegister), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::WriteSingleRegister)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadExceptionStatus), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadExceptionStatus)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::Diagnostics), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::Diagnostics)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::GetCommEventCounter), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::GetCommEventCounter)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::GetCommEventLog), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::GetCommEventLog)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteMultipleCoils), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::WriteMultipleCoils)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteMultipleRegisters), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::WriteMultipleRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReportServerId), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReportServerId)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadFileRecord), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadFileRecord)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteFileRecord), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::WriteFileRecord)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::MaskWriteRegister), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::MaskWriteRegister)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadWriteMultipleRegisters), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadFifoQueue), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::ReadFifoQueue)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::EncapsulatedInterfaceTransport)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::Invalid), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::Invalid)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::UndefinedFunctionCode), request),
++ QModbusRequest::minimumDataSize(QModbusRequest(QModbusRequest::Invalid)));
++
++ request = false;
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadCoils), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadCoils)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadDiscreteInputs), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadDiscreteInputs)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteSingleCoil), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::WriteSingleCoil)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadHoldingRegisters), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadHoldingRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadInputRegisters), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadInputRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteSingleRegister), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::WriteSingleRegister)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadExceptionStatus), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadExceptionStatus)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::Diagnostics), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::Diagnostics)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::GetCommEventCounter), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::GetCommEventCounter)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::GetCommEventLog), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::GetCommEventLog)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteMultipleCoils), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::WriteMultipleCoils)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteMultipleRegisters), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::WriteMultipleRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReportServerId), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReportServerId)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadFileRecord), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadFileRecord)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::WriteFileRecord), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::WriteFileRecord)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::MaskWriteRegister), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::MaskWriteRegister)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadWriteMultipleRegisters), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadWriteMultipleRegisters)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::ReadFifoQueue), request),
++ QModbusResponse::minimumDataSize(QModbusResponse(QModbusResponse::ReadFifoQueue)));
++ QCOMPARE(minimumDataSize(QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport), request),
++ QModbusResponse::minimumDataSize(QModbusRequest(QModbusPdu::EncapsulatedInterfaceTransport)));
++ QCOMPARE(minimumDataSize(QModbusResponse(QModbusPdu::Invalid), request),
++ QModbusRequest::minimumDataSize(QModbusResponse(QModbusPdu::Invalid)));
++ QCOMPARE(minimumDataSize(QModbusResponse(QModbusPdu::UndefinedFunctionCode), request),
++ QModbusRequest::minimumDataSize(QModbusResponse(QModbusPdu::Invalid)));
++
++ QModbusExceptionResponse exception(QModbusResponse::ReadCoils, QModbusPdu::IllegalFunction);
++ QCOMPARE(minimumDataSize(exception, request), 1);
++ QCOMPARE(QModbusResponse::minimumDataSize(exception), 1);
++ }
++
++ void testQModbusRequestStreamOperator_data()
++ {
++ QTest::addColumn<QModbusPdu::FunctionCode>("fc");
++ QTest::addColumn<QByteArray>("data");
++ QTest::addColumn<QByteArray>("pdu");
++
++ QTest::newRow("ReadCoils") << QModbusRequest::ReadCoils << QByteArray::fromHex("00130013")
++ << QByteArray::fromHex("0100130013");
++ QTest::newRow("ReadDiscreteInputs") << QModbusRequest::ReadDiscreteInputs
++ << QByteArray::fromHex("00c40016") << QByteArray::fromHex("0200c40016");
++ QTest::newRow("ReadHoldingRegisters") << QModbusRequest::ReadHoldingRegisters
++ << QByteArray::fromHex("006b0003") << QByteArray::fromHex("03006b0003");
++ QTest::newRow("ReadInputRegisters") << QModbusRequest::ReadInputRegisters
++ << QByteArray::fromHex("00080001") << QByteArray::fromHex("0400080001");
++ QTest::newRow("WriteSingleCoil") << QModbusRequest::WriteSingleCoil
++ << QByteArray::fromHex("00acff00") << QByteArray::fromHex("0500acff00");
++ QTest::newRow("WriteSingleRegister") << QModbusRequest::WriteSingleRegister
++ << QByteArray::fromHex("00010003") << QByteArray::fromHex("0600010003");
++ QTest::newRow("ReadExceptionStatus") << QModbusRequest::ReadExceptionStatus
++ << QByteArray() << QByteArray::fromHex("07");
++ QTest::newRow("Diagnostics") << QModbusRequest::Diagnostics
++ << QByteArray::fromHex("0000a537") << QByteArray::fromHex("080000a537");
++ QTest::newRow("GetCommEventCounter") << QModbusRequest::GetCommEventCounter
++ << QByteArray() << QByteArray::fromHex("0b");
++ QTest::newRow("GetCommEventLog") << QModbusRequest::GetCommEventLog
++ << QByteArray() << QByteArray::fromHex("0c");
++ QTest::newRow("WriteMultipleCoils") << QModbusRequest::WriteMultipleCoils
++ << QByteArray::fromHex("0013000a02cd01") << QByteArray::fromHex("0f0013000a02cd01");
++ QTest::newRow("WriteMultipleRegisters") << QModbusRequest::WriteMultipleRegisters
++ << QByteArray::fromHex("0001000204000a0102") << QByteArray::fromHex("100001000204000a0102");
++ QTest::newRow("ReportServerId") << QModbusRequest::ReportServerId
++ << QByteArray() << QByteArray::fromHex("11");
++ QTest::newRow("ReadFileRecord") << QModbusRequest::ReadFileRecord
++ << QByteArray::fromHex("0e0600040001000206000300090002")
++ << QByteArray::fromHex("140e0600040001000206000300090002");
++ QTest::newRow("WriteFileRecord") << QModbusRequest::WriteFileRecord
++ << QByteArray::fromHex("0d0600040007000306af04be100d")
++ << QByteArray::fromHex("150d0600040007000306af04be100d");
++ QTest::newRow("MaskWriteRegister") << QModbusRequest::MaskWriteRegister
++ << QByteArray::fromHex("000400f20025") << QByteArray::fromHex("16000400f20025");
++ QTest::newRow("ReadWriteMultipleRegisters") << QModbusRequest::ReadWriteMultipleRegisters
++ << QByteArray::fromHex("00030006000e00030600ff00ff00ff")
++ << QByteArray::fromHex("1700030006000e00030600ff00ff00ff");
++ QTest::newRow("ReadFifoQueue") << QModbusRequest::ReadFifoQueue
++ << QByteArray::fromHex("04de") << QByteArray::fromHex("1804de");
++ QTest::newRow("EncapsulatedInterfaceTransport")
++ << QModbusRequest::EncapsulatedInterfaceTransport
++ << QByteArray::fromHex("0e0100") << QByteArray::fromHex("2b0e0100");
++ }
++
++ void testQModbusRequestStreamOperator()
++ {
++ QFETCH(QModbusPdu::FunctionCode, fc);
++ QFETCH(QByteArray, data);
++ QFETCH(QByteArray, pdu);
++
++ QByteArray ba;
++ QDataStream output(&ba, QIODevice::WriteOnly);
++ output << QModbusRequest(fc, data);
++ QCOMPARE(ba, pdu);
++
++ QModbusRequest request;
++ QDataStream input(pdu);
++ input >> request;
++ QCOMPARE(request.functionCode(), fc);
++ QCOMPARE(request.data(), data);
++ }
++
++ void testQModbusResponseStreamOperator_data()
++ {
++ QTest::addColumn<QModbusPdu::FunctionCode>("fc");
++ QTest::addColumn<QByteArray>("data");
++ QTest::addColumn<QByteArray>("pdu");
++
++ QTest::newRow("ReadCoils") << QModbusResponse::ReadCoils << QByteArray::fromHex("03cd6b05")
++ << QByteArray::fromHex("0103cd6b05");
++ QTest::newRow("ReadDiscreteInputs") << QModbusResponse::ReadDiscreteInputs
++ << QByteArray::fromHex("03acdb35") << QByteArray::fromHex("0203acdb35");
++ QTest::newRow("ReadHoldingRegisters") << QModbusResponse::ReadHoldingRegisters
++ << QByteArray::fromHex("06022b00000064") << QByteArray::fromHex("0306022b00000064");
++ QTest::newRow("ReadInputRegisters") << QModbusResponse::ReadInputRegisters
++ << QByteArray::fromHex("02000a") << QByteArray::fromHex("0402000a");
++ QTest::newRow("WriteSingleCoil") << QModbusResponse::WriteSingleCoil
++ << QByteArray::fromHex("00acff00") << QByteArray::fromHex("0500acff00");
++ QTest::newRow("WriteSingleRegister") << QModbusResponse::WriteSingleRegister
++ << QByteArray::fromHex("00010003") << QByteArray::fromHex("0600010003");
++ QTest::newRow("ReadExceptionStatus") << QModbusResponse::ReadExceptionStatus
++ << QByteArray::fromHex("6d") << QByteArray::fromHex("076d");
++ QTest::newRow("Diagnostics") << QModbusResponse::Diagnostics
++ << QByteArray::fromHex("ffff0108") << QByteArray::fromHex("08ffff0108");
++ QTest::newRow("GetCommEventCounter") << QModbusResponse::GetCommEventCounter
++ << QByteArray::fromHex("ffff0108") << QByteArray::fromHex("0bffff0108");
++ QTest::newRow("GetCommEventLog") << QModbusResponse::GetCommEventLog
++ << QByteArray::fromHex("080000010801212000") << QByteArray::fromHex("0c080000010801212000");
++ QTest::newRow("WriteMultipleCoils") << QModbusResponse::WriteMultipleCoils
++ << QByteArray::fromHex("0013000a") << QByteArray::fromHex("0f0013000a");
++ QTest::newRow("WriteMultipleRegisters") << QModbusResponse::WriteMultipleRegisters
++ << QByteArray::fromHex("00010002") << QByteArray::fromHex("1000010002");
++ QTest::newRow("ReportServerId") << QModbusResponse::ReportServerId
++ << QByteArray::fromHex("030aff12") << QByteArray::fromHex("11030aff12");
++ QTest::newRow("ReadFileRecord") << QModbusResponse::ReadFileRecord
++ << QByteArray::fromHex("0c05060dfe0020050633cd0040")
++ << QByteArray::fromHex("140c05060dfe0020050633cd0040");
++ QTest::newRow("WriteFileRecord") << QModbusResponse::WriteFileRecord
++ << QByteArray::fromHex("0d0600040007000306af04be100d")
++ << QByteArray::fromHex("150d0600040007000306af04be100d");
++ QTest::newRow("MaskWriteRegister") << QModbusResponse::MaskWriteRegister
++ << QByteArray::fromHex("000400f20025") << QByteArray::fromHex("16000400f20025");
++ QTest::newRow("ReadWriteMultipleRegisters") << QModbusResponse::ReadWriteMultipleRegisters
++ << QByteArray::fromHex("0c00fe0acd00010003000d00ff")
++ << QByteArray::fromHex("170c00fe0acd00010003000d00ff");
++ QTest::newRow("ReadFifoQueue") << QModbusResponse::ReadFifoQueue
++ << QByteArray::fromHex("0006000201b81284") << QByteArray::fromHex("180006000201b81284");
++ QTest::newRow("EncapsulatedInterfaceTransport")
++ << QModbusResponse::EncapsulatedInterfaceTransport
++ << QByteArray::fromHex("0e01010000030016") + "Company identification" +
++ QByteArray::fromHex("010c") + "Product code" + QByteArray::fromHex("0205") +
++ "V2.11"
++ << QByteArray::fromHex("2b0e01010000030016") + "Company identification" +
++ QByteArray::fromHex("010c") + "Product code" + QByteArray::fromHex("0205") +
++ "V2.11";
++
++ QModbusExceptionResponse ex(QModbusPdu::ReadCoils, QModbusPdu::IllegalDataAddress);
++ QTest::newRow("StreamExceptionResponse")
++ << QModbusPdu::FunctionCode(ex.functionCode() | QModbusPdu::ExceptionByte)
++ << QByteArray::fromHex("02") << QByteArray::fromHex("8102");
++ }
++
++ void testQModbusResponseStreamOperator()
++ {
++ QFETCH(QModbusPdu::FunctionCode, fc);
++ QFETCH(QByteArray, data);
++ QFETCH(QByteArray, pdu);
++
++ QByteArray ba;
++ QDataStream output(&ba, QIODevice::WriteOnly);
++ output << QModbusResponse(fc, data);
++ QCOMPARE(ba, pdu);
++
++ QModbusResponse response;
++ QDataStream input(pdu);
++ input >> response;
++ if (response.isException())
++ QCOMPARE(quint8(response.functionCode()), quint8(fc &~ QModbusPdu::ExceptionByte));
++ else
++ QCOMPARE(response.functionCode(), fc);
++ QCOMPARE(response.data(), data);
++ }
++
++ void testEncapsulatedInterfaceTransportStreamOperator()
++ {
++ const QByteArray valid = QByteArray::fromHex("0e01010000030016") + "Company identification"
++ + QByteArray::fromHex("010c") + "Product code" + QByteArray::fromHex("0205")
++ + "V2.11";
++ {
++ QByteArray ba;
++ QDataStream output(&ba, QIODevice::ReadWrite);
++ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, valid);
++
++ QModbusResponse response;
++ QDataStream input(ba);
++ input >> response;
++ QCOMPARE(response.functionCode(), QModbusResponse::EncapsulatedInterfaceTransport);
++ QCOMPARE(response.data(), valid);
++ }
++
++ // valid embedded
++ const QByteArray suffix = QByteArray::fromHex("ffffffffff");
++ {
++ QByteArray ba;
++ QDataStream output(&ba, QIODevice::ReadWrite);
++ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, valid
++ + suffix);
++
++ QModbusResponse response;
++ QDataStream input(ba);
++ input >> response;
++ QCOMPARE(response.functionCode(), QModbusResponse::EncapsulatedInterfaceTransport);
++ QCOMPARE(response.data(), valid);
++ }
++
++ const QByteArray invalid = QByteArray::fromHex("0e01010000030016") + "Company identification"
++ + QByteArray::fromHex("01ff") + "Product code" + QByteArray::fromHex("0205")
++ + "V2.11";
++ {
++ QByteArray ba;
++ QDataStream output(&ba, QIODevice::ReadWrite);
++ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, invalid);
++
++ QModbusResponse response;
++ QDataStream input(ba);
++ input >> response;
++ QCOMPARE(response.functionCode(), QModbusResponse::Invalid);
++ QCOMPARE(response.data(), QByteArray());
++ }
++
++ const QByteArray invalidShort = QByteArray::fromHex("0e01010000030016")
++ + "Company identification" + QByteArray::fromHex("01");
++ {
++ QByteArray ba;
++ QDataStream output(&ba, QIODevice::ReadWrite);
++ output << QModbusResponse(QModbusResponse::EncapsulatedInterfaceTransport, invalidShort);
++
++ QModbusResponse response;
++ QDataStream input(ba);
++ input >> response;
++ QCOMPARE(response.functionCode(), QModbusResponse::Invalid);
++ QCOMPARE(response.data(), QByteArray());
++ }
++ }
++
++ void testCalculateDataSize()
++ {
++ // CanOpenGeneralReference
++ QModbusResponse cogr(QModbusPdu::EncapsulatedInterfaceTransport, quint8(0x0d));
++ QCOMPARE(QModbusResponse::calculateDataSize(cogr), -1);
++ cogr.encodeData(quint8(0x0d), quint8(0x00));
++ QCOMPARE(QModbusResponse::calculateDataSize(cogr), 2);
++ // TODO: fix the following once we can calculate the size for CanOpenGeneralReference
++ cogr.encodeData(quint8(0x0d), quint8(0x00), quint8(0x00));
++ QCOMPARE(QModbusResponse::calculateDataSize(cogr), 2);
++
++ // ReadDeviceIdentification
++ QModbusResponse rdi(QModbusPdu::EncapsulatedInterfaceTransport, quint8(0x0e));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), -1);
++ rdi.encodeData(quint8(0x0e), quint8(0x01));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++
++ // single object
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x01)); // object count
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x01),
++ quint8(0x00)); // object id
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x01),
++ quint8(0x00), quint8(0x01)); // object size
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 9);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x01),
++ quint8(0x00), quint8(0x01), quint8(0xff)); // object data
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 9);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x01),
++ quint8(0x00), quint8(0x01), quint8(0xff),
++ quint8(0xff)); // dummy additional data
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 9);
++
++ // multiple objects equal size
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
++ quint8(0x02)); // object count
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
++ quint8(0x02),
++ quint8(0x00)); // object id
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x02),
++ quint8(0x00), quint8(0x01)); // object size
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 11);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x02),
++ quint8(0x00), quint8(0x01), quint8(0xff)); // object data
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 11);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x02),
++ quint8(0x00), quint8(0x01), quint8(0xff),
++ quint8(0x01)); // second object id
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 11);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x02),
++ quint8(0x00), quint8(0x01), quint8(0xff),
++ quint8(0x01), quint8(0x01)); // second object size
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 12);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x02),
++ quint8(0x00), quint8(0x01), quint8(0xff),
++ quint8(0x01), quint8(0x01), quint8(0xff)); // second object data
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 12);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x02),
++ quint8(0x00), quint8(0x01), quint8(0xff),
++ quint8(0x01), quint8(0x01), quint8(0xff),
++ quint8(0xff)); // dummy additional data
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 12);
++
++ // multiple objects different size
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
++ quint8(0x03));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0xff), quint8(0x02),
++ quint8(0x03),
++ quint8(0x00));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 8);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x01));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 13);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 14);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 17);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 17);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 17);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff));
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ rdi.encodeData(quint8(0x0e), quint8(0x01), quint8(0x01), quint8(0x00), quint8(0x00),
++ quint8(0x03),
++ quint8(0x00), quint8(0x02), quint8(0xff), quint8(0xff),
++ quint8(0x01), quint8(0x03), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0x02), quint8(0x05), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff), quint8(0xff),
++ quint8(0xff)); // dummy additional data
++ QCOMPARE(QModbusResponse::calculateDataSize(rdi), 22);
++ }
++
++ void testCustomCalculator()
++ {
++ // request
++ QModbusRequest request(QModbusRequest::WriteSingleCoil);
++ request.encodeData(quint8(0x00), quint8(0xac), quint8(0xff), quint8(0x00));
++ QCOMPARE(QModbusRequest::calculateDataSize(request), 4);
++
++ QModbusRequest::registerDataSizeCalculator(QModbusRequest::WriteSingleCoil,
++ [](const QModbusRequest &)->int {
++ return 15;
++ });
++ QCOMPARE(QModbusRequest::calculateDataSize(request), 15);
++
++ QModbusRequest::registerDataSizeCalculator(QModbusRequest::WriteSingleCoil, nullptr);
++ QCOMPARE(QModbusRequest::calculateDataSize(request), 4);
++
++ // response
++ QModbusResponse response(QModbusRequest::WriteSingleCoil);
++ response.encodeData(quint8(0x00), quint8(0xac), quint8(0xff), quint8(0x00));
++ QCOMPARE(QModbusResponse::calculateDataSize(request), 4);
++
++ QModbusResponse::registerDataSizeCalculator(QModbusResponse::WriteSingleCoil,
++ [](const QModbusResponse &)->int {
++ return 15;
++ });
++ QCOMPARE(QModbusResponse::calculateDataSize(response), 15);
++
++ QModbusResponse::registerDataSizeCalculator(QModbusResponse::WriteSingleCoil, nullptr);
++ QCOMPARE(QModbusResponse::calculateDataSize(response), 4);
++
++ // custom
++ QModbusPdu::FunctionCode custom = QModbusPdu::FunctionCode(0x30);
++ request.setFunctionCode(custom);
++ QCOMPARE(QModbusRequest::calculateDataSize(request), -1);
++
++ QModbusRequest::registerDataSizeCalculator(custom, [](const QModbusRequest &)->int {
++ return 27;
++ });
++ QCOMPARE(QModbusRequest::calculateDataSize(request), 27);
++ }
++
++ void testCalculateLongDataSize()
++ {
++ QByteArray longData = QByteArray(128, ' ');
++ longData[0] = longData.size();
++ const QModbusResponse response(QModbusResponse::ReadCoils, longData);
++ QCOMPARE(QModbusResponse::calculateDataSize(response), 1 + longData.size());
++
++ const QModbusRequest wfrRequest(QModbusPdu::WriteFileRecord, longData);
++ QCOMPARE(QModbusRequest::calculateDataSize(wfrRequest), 1 + longData.size());
++
++ longData = QByteArray(4 + 128, ' ');
++ longData[4] = char(128);
++ const QModbusRequest wmcRequest(QModbusPdu::WriteMultipleCoils, longData);
++ QCOMPARE(QModbusRequest::calculateDataSize(wmcRequest), 1 + longData.size());
++
++ const QModbusRequest wmrRequest(QModbusPdu::WriteMultipleRegisters, longData);
++ QCOMPARE(QModbusRequest::calculateDataSize(wmrRequest), 1 + longData.size());
++ }
++ };
++
++ QTEST_MAIN(tst_QModbusPdu)
++
++ #include "tst_qmodbuspdu.moc"