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 "qmodbusdeviceidentification.h"
38 #include "qmodbusserver.h"
39 #include "qmodbusserver_p.h"
40 #include "qmodbus_symbols_p.h"
41 
42 #include <QtCore/qbitarray.h>
43 #include <QtCore/qdebug.h>
44 #include <QtCore/qloggingcategory.h>
45 #include <QtCore/qvector.h>
46 
47 #include <algorithm>
48 
49 QT_BEGIN_NAMESPACE
50 
Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)51 Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS)
52 
53 /*!
54     \class QModbusServer
55     \inmodule QtSerialBus
56     \since 5.8
57 
58     \brief The QModbusServer class is the interface to receive and process Modbus requests.
59 
60     Modbus networks can have multiple Modbus servers. Modbus Servers are read/written by a
61     Modbus client represented by \l QModbusClient. QModbusServer communicates with a Modbus
62     backend, providing users with a convenient API.
63 */
64 
65 /*!
66     \enum QModbusServer::Option
67 
68     Each Modbus server has a set of values associated with it, each with its own option.
69 
70     The general purpose options (and the associated types) are:
71 
72     \value DiagnosticRegister       The diagnostic register of the server. \c quint16
73     \value ExceptionStatusOffset    The exception status byte offset of the server. \c quint16
74     \value DeviceBusy               Flag to signal the server is engaged in processing a
75                                     long-duration program command. \c quint16
76     \value AsciiInputDelimiter      The Modbus ASCII end of message delimiter. \c char
77     \value ListenOnlyMode           Flag to set listen only mode of the server.
78                                     This function is typically supported only
79                                     by Modbus serial devices. \c bool
80     \value ServerIdentifier         The identifier of the server, \b not the server address. \c quint8
81     \value RunIndicatorStatus       The run indicator of the server. \c quint8
82     \value AdditionalData           The additional data of the server. \c QByteArray
83     \value DeviceIdentification     The physical and functional description of the server. \c QModbusDeviceIdentification
84 
85     User options:
86 
87     \value UserOption               The first option that can be used for user-specific purposes.
88 
89     For user options, it is up to the developer to decide which types to use and ensure that
90     components use the correct types when accessing and setting values.
91 */
92 
93 /*!
94     Constructs a Modbus server with the specified \a parent.
95 */
96 QModbusServer::QModbusServer(QObject *parent) :
97     QModbusDevice(*new QModbusServerPrivate, parent)
98 {
99 }
100 
101 /*!
102     \internal
103 */
~QModbusServer()104 QModbusServer::~QModbusServer()
105 {
106 }
107 
108 /*!
109     \internal
110 */
QModbusServer(QModbusServerPrivate & dd,QObject * parent)111 QModbusServer::QModbusServer(QModbusServerPrivate &dd, QObject *parent) :
112     QModbusDevice(dd, parent)
113 {
114 }
115 
116 /*!
117     Sets the registered map structure for requests from other ModBus clients to \a map.
118     The register values are initialized with zero. Returns \c true on success; otherwise \c false.
119 
120     If this function is not called before connecting, a default register with zero
121     entries is setup.
122 
123     \note Calling this function discards any register value that was previously set.
124 */
setMap(const QModbusDataUnitMap & map)125 bool QModbusServer::setMap(const QModbusDataUnitMap &map)
126 {
127     return d_func()->setMap(map);
128 }
129 
130 /*!
131     Sets the address for this Modbus server instance to \a serverAddress.
132 
133     \sa serverAddress()
134 */
setServerAddress(int serverAddress)135 void QModbusServer::setServerAddress(int serverAddress)
136 {
137     Q_D(QModbusServer);
138     d->m_serverAddress = serverAddress;
139 }
140 
141 /*!
142     Returns the address of this Mobus server instance.
143 
144     \sa setServerAddress()
145 */
serverAddress() const146 int QModbusServer::serverAddress() const
147 {
148     Q_D(const QModbusServer);
149 
150     return d->m_serverAddress;
151 }
152 
153 /*!
154     Returns the value for \a option or an invalid \c QVariant if the option is
155     not set.
156 
157     \table
158         \header
159             \li Option
160             \li Description
161         \row
162             \li \l QModbusServer::DiagnosticRegister
163             \li Returns the diagnostic register value of the server. The
164                 diagnostic register contains device specific contents where
165                 each bit has a specific meaning.
166         \row
167             \li \l QModbusServer::ExceptionStatusOffset
168             \li Returns the offset address of the exception status byte
169                 location in the coils register.
170         \row
171             \li \l QModbusServer::DeviceBusy
172             \li Returns a flag that signals if the server is engaged in
173                 processing a long-duration program command.
174         \row
175             \li \l QModbusServer::AsciiInputDelimiter
176             \li Returns a end of message delimiter of Modbus ASCII messages.
177         \row
178             \li \l QModbusServer::ListenOnlyMode
179             \li Returns the server's listen only state. Messages are monitored
180                 but no response will be sent.
181         \row
182             \li \l QModbusServer::ServerIdentifier
183             \li Returns the server manufacturer's identifier code. This can be
184                 an arbitrary value in the range of \c 0x00 to 0xff.
185         \row
186             \li \l QModbusServer::RunIndicatorStatus
187             \li Returns the server's run indicator status. This data is used as
188                 addendum by the \l QModbusPdu::ReportServerId function code.
189         \row
190             \li \l QModbusServer::AdditionalData
191             \li Returns the server's additional data. This data is used as
192                 addendum by the \l QModbusPdu::ReportServerId function code.
193         \row
194             \li \l QModbusServer::DeviceIdentification
195             \li Returns the server's physical and functional description.
196         \row
197             \li \l QModbusServer::UserOption
198             \li Returns the value of a user option.
199 
200                 \note For user options, it is up to the developer to decide
201                 which types to use and ensure that components use the correct
202                 types when accessing and setting values.
203     \endtable
204 */
value(int option) const205 QVariant QModbusServer::value(int option) const
206 {
207     Q_D(const QModbusServer);
208 
209     switch (option) {
210         case DiagnosticRegister:
211             return d->m_serverOptions.value(option, quint16(0x0000));
212         case ExceptionStatusOffset:
213             return d->m_serverOptions.value(option, quint16(0x0000));
214         case DeviceBusy:
215             return d->m_serverOptions.value(option, quint16(0x0000));
216         case AsciiInputDelimiter:
217             return d->m_serverOptions.value(option, '\n');
218         case ListenOnlyMode:
219             return d->m_serverOptions.value(option, false);
220         case ServerIdentifier:
221             return d->m_serverOptions.value(option, quint8(0x0a));
222         case RunIndicatorStatus:
223             return d->m_serverOptions.value(option, quint8(0xff));
224         case AdditionalData:
225             return d->m_serverOptions.value(option, QByteArray("Qt Modbus Server"));
226         case DeviceIdentification:
227             return d->m_serverOptions.value(option, QVariant());
228     };
229 
230     if (option < UserOption)
231         return QVariant();
232 
233     return d->m_serverOptions.value(option, QVariant());
234 }
235 
236 /*!
237     Sets the \a newValue for \a option and returns \c true on success; \c false
238     otherwise.
239 
240     \note If the option's associated type is \c quint8 or \c quint16 and the
241     type of \a newValue is larger, the data will be truncated or conversation
242     will fail.
243 
244     \table
245         \header
246             \li Key
247             \li Description
248         \row
249             \li \l QModbusServer::DiagnosticRegister
250             \li Sets the diagnostic register of the server in a device specific
251                 encoding to \a newValue. The default value preset is \c 0x0000.
252                 The bit values of the register need device specific documentation.
253         \row
254             \li \l QModbusServer::ExceptionStatusOffset
255             \li Sets the exception status byte offset of the server to
256                 \a newValue which is the absolute offset address in the coils
257                 (0x register). Modbus register table starting with \c 0x0000h.
258                 The default value preset is \c 0x0000, using the exception
259                 status coils similar to Modicon 984 CPUs (coils 1-8).
260 
261                 The function returns \c true if the coils register contains the
262                 8 bits required for storing and retrieving the status coils,
263                 otherwise \c false.
264         \row
265             \li \l QModbusServer::DeviceBusy
266             \li Sets a flag that signals that the server is engaged in
267                 processing a long-duration program command. Valid values are
268                 \c 0x0000 (not busy) and \c 0xffff (busy).
269                 The default value preset is \c 0x0000.
270         \row
271             \li \l QModbusServer::AsciiInputDelimiter
272             \li The \a newValue becomes the end of message delimiter for future
273                 Modbus ASCII messages. The default value preset is \c {\n}.
274         \row
275             \li \l QModbusServer::ListenOnlyMode
276             \li Ss the server's listen only state to \a newValue. If listen only
277                 mode is set to \c true, messages are monitored but no response
278                 will be sent. The default value preset is \c false.
279         \row
280             \li \l QModbusServer::ServerIdentifier
281             \li Sets the server's manufacturer identifier to \a newValue.
282                 Possible values are in the range of \c 0x00 to 0xff.
283                 The default value preset is \c 0x0a.
284         \row
285             \li \l QModbusServer::RunIndicatorStatus
286             \li Sets the servers' run indicator status to \a newValue. This
287                 data is used as addendum by the \l QModbusPdu::ReportServerId
288                 function code. Valid values are \c 0x00 (OFF) and \c 0xff (ON).
289                 The default value preset is \c 0xff (ON).
290         \row
291             \li \l QModbusServer::AdditionalData
292             \li Sets the server's additional data to \a newValue. This data is
293                 used as addendum by the \l QModbusPdu::ReportServerId function
294                 code. The maximum data size cannot exceed 249 bytes to match
295                 response message size restrictions.
296                 The default value preset is \c {Qt Modbus Server}.
297         \row
298             \li \l QModbusServer::DeviceIdentification
299             \li Sets the server's physical and functional description. By default
300                 there is no additional device identification data set.
301         \row
302             \li \l QModbusServer::UserOption
303             \li Sets the value of a user option to \a newValue.
304 
305                 \note For user options, it is up to the developer to decide
306                 which types to use and ensure that components use the correct
307                 types when accessing and setting values.
308     \endtable
309 */
setValue(int option,const QVariant & newValue)310 bool QModbusServer::setValue(int option, const QVariant &newValue)
311 {
312 #define CHECK_INT_OR_UINT(val) \
313     do { \
314         if ((val.type() != QVariant::Int) && (val.type() != QVariant::UInt)) \
315             return false; \
316     } while (0)
317 
318     Q_D(QModbusServer);
319     switch (option) {
320     case DiagnosticRegister:
321         CHECK_INT_OR_UINT(newValue);
322         d->m_serverOptions.insert(option, newValue);
323         return true;
324     case ExceptionStatusOffset: {
325         CHECK_INT_OR_UINT(newValue);
326         const quint16 tmp = newValue.value<quint16>();
327         QModbusDataUnit coils(QModbusDataUnit::Coils, tmp, 8);
328         if (!data(&coils))
329             return false;
330         d->m_serverOptions.insert(option, tmp);
331         return true;
332     }
333     case DeviceBusy: {
334         CHECK_INT_OR_UINT(newValue);
335         const quint16 tmp = newValue.value<quint16>();
336         if ((tmp != 0x0000) && (tmp != 0xffff))
337             return false;
338         d->m_serverOptions.insert(option, tmp);
339         return true;
340     }
341     case AsciiInputDelimiter: {
342         CHECK_INT_OR_UINT(newValue);
343         bool ok = false;
344         if (newValue.toUInt(&ok) > 0xff || !ok)
345             return false;
346         d->m_serverOptions.insert(option, newValue);
347         return true;
348     }
349     case ListenOnlyMode: {
350         if (newValue.type() != QVariant::Bool)
351             return false;
352         d->m_serverOptions.insert(option, newValue);
353         return true;
354     }
355     case ServerIdentifier:
356         CHECK_INT_OR_UINT(newValue);
357         d->m_serverOptions.insert(option, newValue);
358         return true;
359     case RunIndicatorStatus: {
360         CHECK_INT_OR_UINT(newValue);
361         const quint8 tmp = newValue.value<quint8>();
362         if ((tmp != 0x00) && (tmp != 0xff))
363             return false;
364         d->m_serverOptions.insert(option, tmp);
365         return true;
366     }
367     case AdditionalData: {
368         if (newValue.type() != QVariant::ByteArray)
369             return false;
370         const QByteArray additionalData = newValue.toByteArray();
371         if (additionalData.size() > 249)
372             return false;
373         d->m_serverOptions.insert(option, additionalData);
374         return true;
375     }
376     case DeviceIdentification:
377         if (!newValue.canConvert<QModbusDeviceIdentification>())
378             return false;
379         d->m_serverOptions.insert(option, newValue);
380         return true;
381     default:
382         break;
383     };
384 
385     if (option < UserOption)
386         return false;
387     d->m_serverOptions.insert(option, newValue);
388     return true;
389 
390 #undef CHECK_INT_OR_UINT
391 }
392 
393 /*!
394     \fn bool QModbusServer::processesBroadcast() const
395 
396     Subclasses should implement this function if the transport layer shall handle broadcasts.
397     The implementation then should return \c true if the currently processed request is a
398     broadcast request; otherwise \c false. The default implementation returns always \c false.
399 
400     \note The return value of this function only makes sense from within processRequest() or
401     processPrivateRequest(), otherwise it can only tell that the last request processed
402     was a broadcast request.
403 */
404 
405 /*!
406     Reads data stored in the Modbus server. A Modbus server has four tables (\a table) and each
407     have a unique \a address field, which is used to read \a data from the desired field.
408     See QModbusDataUnit::RegisterType for more information about the different tables.
409     Returns \c false if address is outside of the map range or the register type is not even defined.
410 
411     \sa QModbusDataUnit::RegisterType, setData()
412 */
data(QModbusDataUnit::RegisterType table,quint16 address,quint16 * data) const413 bool QModbusServer::data(QModbusDataUnit::RegisterType table, quint16 address, quint16 *data) const
414 {
415     QModbusDataUnit unit(table, address, 1u);
416     if (data && readData(&unit)) {
417         *data = unit.value(0);
418         return true;
419     }
420     return false;
421 }
422 
423 /*!
424     Returns the values in the register range given by \a newData.
425 
426     \a newData must provide a valid register type, start address
427     and valueCount. The returned \a newData will contain the register values
428     associated with the given range.
429 
430     If \a newData contains a valid register type but a negative start address
431     the entire register map is returned and \a newData appropriately sized.
432 */
data(QModbusDataUnit * newData) const433 bool QModbusServer::data(QModbusDataUnit *newData) const
434 {
435     return readData(newData);
436 }
437 
438 /*!
439     Writes data to the Modbus server. A Modbus server has four tables (\a table) and each have a
440     unique \a address field, which is used to write \a data to the desired field.
441     Returns \c false if address outside of the map range.
442 
443     If the call was successful the \l dataWritten() signal is emitted. Note that
444     the signal is not emitted when \a data has not changed. Nevertheless this function
445     returns \c true in such cases.
446 
447     \sa QModbusDataUnit::RegisterType, data(), dataWritten()
448 */
setData(QModbusDataUnit::RegisterType table,quint16 address,quint16 data)449 bool QModbusServer::setData(QModbusDataUnit::RegisterType table, quint16 address, quint16 data)
450 {
451     return writeData(QModbusDataUnit(table, address, QVector<quint16>() << data));
452 }
453 
454 /*!
455     Writes \a newData to the Modbus server map.
456     Returns \c false if the \a newData range is outside of the map range.
457 
458     If the call was successful the \l dataWritten() signal is emitted. Note that
459     the signal is not emitted when the addressed register has not changed. This
460     may happen when \a newData contains exactly the same values as the
461     register already. Nevertheless this function returns \c true in such cases.
462 
463     \sa data()
464 */
setData(const QModbusDataUnit & newData)465 bool QModbusServer::setData(const QModbusDataUnit &newData)
466 {
467     return writeData(newData);
468 }
469 
470 /*!
471     Writes \a newData to the Modbus server map. Returns \c true on success,
472     or \c false if the \a newData range is outside of the map range or the
473     registerType() does not exist.
474 
475     \note Sub-classes that implement writing to a different backing store
476     then default one, also need to implement setMap() and readData(). The
477     dataWritten() signal needs to be emitted from within the functions
478     implementation as well.
479 
480     \sa setMap(), readData(), dataWritten()
481 */
writeData(const QModbusDataUnit & newData)482 bool QModbusServer::writeData(const QModbusDataUnit &newData)
483 {
484     Q_D(QModbusServer);
485     if (!d->m_modbusDataUnitMap.contains(newData.registerType()))
486         return false;
487 
488     QModbusDataUnit &current = d->m_modbusDataUnitMap[newData.registerType()];
489     if (!current.isValid())
490         return false;
491 
492     // check range start is within internal map range
493     int internalRangeEndAddress = current.startAddress() + current.valueCount() - 1;
494     if (newData.startAddress() < current.startAddress()
495         || newData.startAddress() > internalRangeEndAddress) {
496         return false;
497     }
498 
499     // check range end is within internal map range
500     int rangeEndAddress = newData.startAddress() + newData.valueCount() - 1;
501     if (rangeEndAddress < current.startAddress() || rangeEndAddress > internalRangeEndAddress)
502         return false;
503 
504     bool changeRequired = false;
505     for (uint i = 0; i < newData.valueCount(); i++) {
506         const quint16 newValue = newData.value(i);
507         const int translatedIndex = newData.startAddress() - current.startAddress() + i;
508         changeRequired |= (current.value(translatedIndex) != newValue);
509         current.setValue(translatedIndex, newValue);
510     }
511 
512     if (changeRequired)
513         emit dataWritten(newData.registerType(), newData.startAddress(), newData.valueCount());
514     return true;
515 }
516 
517 /*!
518     Reads the values in the register range given by \a newData and writes the
519     data back to \a newData. Returns \c true on success or \c false if
520     \a newData is \c 0, the \a newData range is outside of the map range or the
521     registerType() does not exist.
522 
523     \note Sub-classes that implement reading from a different backing store
524     then default one, also need to implement setMap() and writeData().
525 
526     \sa setMap(), writeData()
527 */
readData(QModbusDataUnit * newData) const528 bool QModbusServer::readData(QModbusDataUnit *newData) const
529 {
530     Q_D(const QModbusServer);
531 
532     if ((!newData) || (!d->m_modbusDataUnitMap.contains(newData->registerType())))
533         return false;
534 
535     const QModbusDataUnit &current = d->m_modbusDataUnitMap.value(newData->registerType());
536     if (!current.isValid())
537         return false;
538 
539      // return entire map for given type
540     if (newData->startAddress() < 0) {
541         *newData = current;
542         return true;
543     }
544 
545     // check range start is within internal map range
546     int internalRangeEndAddress = current.startAddress() + current.valueCount() - 1;
547     if (newData->startAddress() < current.startAddress()
548         || newData->startAddress() > internalRangeEndAddress) {
549         return false;
550     }
551 
552     // check range end is within internal map range
553     const int rangeEndAddress = newData->startAddress() + newData->valueCount() - 1;
554     if (rangeEndAddress < current.startAddress() || rangeEndAddress > internalRangeEndAddress)
555         return false;
556 
557     newData->setValues(current.values().mid(newData->startAddress() - current.startAddress(), newData->valueCount()));
558     return true;
559 }
560 
561 /*!
562     \fn void QModbusServer::dataWritten(QModbusDataUnit::RegisterType table, int address, int size)
563 
564     This signal is emitted when a Modbus client has written one or more fields of data to the
565     Modbus server. The signal contains information about the fields that were written:
566     \list
567         \li Register type (\a table) that was written,
568         \li \a address of the first field that was written,
569         \li and \a size of consecutive fields that were written starting from \a address.
570     \endlist
571 
572     The signal is not emitted when the to-be-written fields have not changed
573     due to no change in value.
574 */
575 
576 /*!
577     Processes a Modbus client \a request and returns a Modbus response.
578     This function returns a \l QModbusResponse or \l QModbusExceptionResponse depending
579     on the nature of the request.
580 
581     The default implementation of this function handles all standard Modbus
582     function codes as defined by the Modbus Application Protocol Specification 1.1b.
583     All other Modbus function codes not included in the specification are forwarded to
584     \l processPrivateRequest().
585 
586     The default handling of the standard Modbus function code requests can be overwritten
587     by reimplementing this function. The override must handle the request type
588     in question and return the appropriate \l QModbusResponse. A common reason might be to
589     filter out function code requests for data values to limit read/write access and
590     function codes not desired in particular implementations such as serial line diagnostics
591     on ethernet or Modbus Plus transport layers. Every other request type should be
592     forwarded to this default implementation.
593 
594     \note This function should not be overridden to provide a custom implementation for
595     non-standard Modbus request types.
596 
597     \sa processPrivateRequest()
598 */
processRequest(const QModbusPdu & request)599 QModbusResponse QModbusServer::processRequest(const QModbusPdu &request)
600 {
601     return d_func()->processRequest(request);
602 }
603 
604 /*!
605     This function should be implemented by custom Modbus servers. It is
606     called by \l processRequest() if the given \a request is not a standard
607     Modbus request.
608 
609     Overwriting this function allows handling of additional function codes and
610     subfunction-codes not specified in the Modbus Application Protocol
611     Specification 1.1b. Reimplementations should call this function again to
612     ensure an exception response is returned for all unknown function codes the
613     custom Modbus implementation does not handle.
614 
615     This default implementation returns a \c QModbusExceptionResponse with the
616     \a request function code and error code set to illegal function.
617 
618     \sa processRequest()
619 */
processPrivateRequest(const QModbusPdu & request)620 QModbusResponse QModbusServer::processPrivateRequest(const QModbusPdu &request)
621 {
622     return QModbusExceptionResponse(request.functionCode(),
623         QModbusExceptionResponse::IllegalFunction);
624 }
625 
626 // -- QModbusServerPrivate
627 
setMap(const QModbusDataUnitMap & map)628 bool QModbusServerPrivate::setMap(const QModbusDataUnitMap &map)
629 {
630     m_modbusDataUnitMap = map;
631     return true;
632 }
633 
processRequest(const QModbusPdu & request)634 QModbusResponse QModbusServerPrivate::processRequest(const QModbusPdu &request)
635 {
636     switch (request.functionCode()) {
637     case QModbusRequest::ReadCoils:
638         return processReadCoilsRequest(request);
639     case QModbusRequest::ReadDiscreteInputs:
640         return processReadDiscreteInputsRequest(request);
641     case QModbusRequest::ReadHoldingRegisters:
642         return processReadHoldingRegistersRequest(request);
643     case QModbusRequest::ReadInputRegisters:
644         return processReadInputRegistersRequest(request);
645     case QModbusRequest::WriteSingleCoil:
646         return processWriteSingleCoilRequest(request);
647     case QModbusRequest::WriteSingleRegister:
648         return processWriteSingleRegisterRequest(request);
649     case QModbusRequest::ReadExceptionStatus:
650         return processReadExceptionStatusRequest(request);
651     case QModbusRequest::Diagnostics:
652         return processDiagnosticsRequest(request);
653     case QModbusRequest::GetCommEventCounter:
654         return processGetCommEventCounterRequest(request);
655     case QModbusRequest::GetCommEventLog:
656         return processGetCommEventLogRequest(request);
657     case QModbusRequest::WriteMultipleCoils:
658         return processWriteMultipleCoilsRequest(request);
659     case QModbusRequest::WriteMultipleRegisters:
660         return processWriteMultipleRegistersRequest(request);
661     case QModbusRequest::ReportServerId:
662         return processReportServerIdRequest(request);
663     case QModbusRequest::ReadFileRecord:    // TODO: Implement.
664     case QModbusRequest::WriteFileRecord:   // TODO: Implement.
665         return QModbusExceptionResponse(request.functionCode(),
666             QModbusExceptionResponse::IllegalFunction);
667     case QModbusRequest::MaskWriteRegister:
668         return processMaskWriteRegisterRequest(request);
669     case QModbusRequest::ReadWriteMultipleRegisters:
670         return processReadWriteMultipleRegistersRequest(request);
671     case QModbusRequest::ReadFifoQueue:
672         return processReadFifoQueueRequest(request);
673     case QModbusRequest::EncapsulatedInterfaceTransport:
674         return processEncapsulatedInterfaceTransportRequest(request);
675     default:
676         break;
677     }
678     return q_func()->processPrivateRequest(request);
679 }
680 
681 #define CHECK_SIZE_EQUALS(req) \
682     do { \
683         if (req.dataSize() != QModbusRequest::minimumDataSize(req)) { \
684             qCDebug(QT_MODBUS) << "(Server) The request's data size does not equal the expected size."; \
685             return QModbusExceptionResponse(req.functionCode(), \
686                                             QModbusExceptionResponse::IllegalDataValue); \
687         } \
688     } while (0)
689 
690 #define CHECK_SIZE_LESS_THAN(req) \
691     do { \
692         if (req.dataSize() < QModbusRequest::minimumDataSize(req)) { \
693             qCDebug(QT_MODBUS) << "(Server) The request's data size is less than the expected size."; \
694             return QModbusExceptionResponse(req.functionCode(), \
695                                             QModbusExceptionResponse::IllegalDataValue); \
696         } \
697     } while (0)
698 
processReadCoilsRequest(const QModbusRequest & request)699 QModbusResponse QModbusServerPrivate::processReadCoilsRequest(const QModbusRequest &request)
700 {
701     return readBits(request, QModbusDataUnit::Coils);
702 }
703 
processReadDiscreteInputsRequest(const QModbusRequest & rqst)704 QModbusResponse QModbusServerPrivate::processReadDiscreteInputsRequest(const QModbusRequest &rqst)
705 {
706     return readBits(rqst, QModbusDataUnit::DiscreteInputs);
707 }
708 
readBits(const QModbusPdu & request,QModbusDataUnit::RegisterType unitType)709 QModbusResponse QModbusServerPrivate::readBits(const QModbusPdu &request,
710                                                QModbusDataUnit::RegisterType unitType)
711 {
712     CHECK_SIZE_EQUALS(request);
713     quint16 address, count;
714     request.decodeData(&address, &count);
715 
716     if ((count < 0x0001) || (count > 0x07D0)) {
717         return QModbusExceptionResponse(request.functionCode(),
718             QModbusExceptionResponse::IllegalDataValue);
719     }
720 
721     // Get the requested range out of the registers.
722     QModbusDataUnit unit(unitType, address, count);
723     if (!q_func()->data(&unit)) {
724         return QModbusExceptionResponse(request.functionCode(),
725             QModbusExceptionResponse::IllegalDataAddress);
726     }
727 
728     quint8 byteCount = count / 8;
729     if ((count % 8) != 0) {
730         byteCount += 1;
731         // If the range is not a multiple of 8, resize.
732         unit.setValueCount(byteCount * 8);
733     }
734 
735     // Using byteCount * 8 so the remaining bits in the last byte are zero
736     QBitArray bytes(byteCount * 8);
737 
738     address = 0; // The data range now starts with zero.
739     for ( ; address < count; ++address)
740         bytes.setBit(address, unit.value(address));
741 
742     QByteArray payload = QByteArray::fromRawData(bytes.bits(), byteCount);
743     payload.prepend(char(byteCount));
744     return QModbusResponse(request.functionCode(), payload);
745 }
746 
processReadHoldingRegistersRequest(const QModbusRequest & rqst)747 QModbusResponse QModbusServerPrivate::processReadHoldingRegistersRequest(const QModbusRequest &rqst)
748 {
749     return readBytes(rqst, QModbusDataUnit::HoldingRegisters);
750 }
751 
processReadInputRegistersRequest(const QModbusRequest & rqst)752 QModbusResponse QModbusServerPrivate::processReadInputRegistersRequest(const QModbusRequest &rqst)
753 {
754     return readBytes(rqst, QModbusDataUnit::InputRegisters);
755 }
756 
readBytes(const QModbusPdu & request,QModbusDataUnit::RegisterType unitType)757 QModbusResponse QModbusServerPrivate::readBytes(const QModbusPdu &request,
758                                                 QModbusDataUnit::RegisterType unitType)
759 {
760     CHECK_SIZE_EQUALS(request);
761     quint16 address, count;
762     request.decodeData(&address, &count);
763 
764     if ((count < 0x0001) || (count > 0x007D)) {
765         return QModbusExceptionResponse(request.functionCode(),
766             QModbusExceptionResponse::IllegalDataValue);
767     }
768 
769     // Get the requested range out of the registers.
770     QModbusDataUnit unit(unitType, address, count);
771     if (!q_func()->data(&unit)) {
772         return QModbusExceptionResponse(request.functionCode(),
773             QModbusExceptionResponse::IllegalDataAddress);
774     }
775 
776     return QModbusResponse(request.functionCode(), quint8(count * 2), unit.values());
777 }
778 
processWriteSingleCoilRequest(const QModbusRequest & request)779 QModbusResponse QModbusServerPrivate::processWriteSingleCoilRequest(const QModbusRequest &request)
780 {
781     return writeSingle(request, QModbusDataUnit::Coils);
782 }
783 
processWriteSingleRegisterRequest(const QModbusRequest & rqst)784 QModbusResponse QModbusServerPrivate::processWriteSingleRegisterRequest(const QModbusRequest &rqst)
785 {
786     return writeSingle(rqst, QModbusDataUnit::HoldingRegisters);
787 }
788 
writeSingle(const QModbusPdu & request,QModbusDataUnit::RegisterType unitType)789 QModbusResponse QModbusServerPrivate::writeSingle(const QModbusPdu &request,
790                                                   QModbusDataUnit::RegisterType unitType)
791 {
792     CHECK_SIZE_EQUALS(request);
793     quint16 address, value;
794     request.decodeData(&address, &value);
795 
796     if ((unitType == QModbusDataUnit::Coils) && ((value != Coil::Off) && (value != Coil::On))) {
797         return QModbusExceptionResponse(request.functionCode(),
798             QModbusExceptionResponse::IllegalDataValue);
799     }
800 
801     quint16 reg;   // Get the requested register, but deliberately ignore.
802     if (!q_func()->data(unitType, address, &reg)) {
803         return QModbusExceptionResponse(request.functionCode(),
804             QModbusExceptionResponse::IllegalDataAddress);
805     }
806 
807     if (!q_func()->setData(unitType, address, value)) {
808         return QModbusExceptionResponse(request.functionCode(),
809             QModbusExceptionResponse::ServerDeviceFailure);
810     }
811 
812     return QModbusResponse(request.functionCode(), address, value);
813 }
814 
processReadExceptionStatusRequest(const QModbusRequest & request)815 QModbusResponse QModbusServerPrivate::processReadExceptionStatusRequest(const QModbusRequest &request)
816 {
817     CHECK_SIZE_EQUALS(request);
818 
819     // Get the requested range out of the registers.
820     const QVariant tmp = q_func()->value(QModbusServer::ExceptionStatusOffset);
821     if (tmp.isNull() || (!tmp.isValid())) {
822         return QModbusExceptionResponse(request.functionCode(),
823             QModbusExceptionResponse::ServerDeviceFailure);
824     }
825     const quint16 exceptionStatusOffset = tmp.value<quint16>();
826     QModbusDataUnit coils(QModbusDataUnit::Coils, exceptionStatusOffset, 8);
827     if (!q_func()->data(&coils)) {
828         return QModbusExceptionResponse(request.functionCode(),
829                                         QModbusExceptionResponse::IllegalDataAddress);
830     }
831 
832     quint16 address = 0;
833     quint8 byte = 0;
834     for (int currentBit = 0; currentBit < 8; ++currentBit)
835         if (coils.value(address++)) // The padding happens inside value().
836             byte |= (1U << currentBit);
837 
838     return QModbusResponse(request.functionCode(), byte);
839 }
840 
processDiagnosticsRequest(const QModbusRequest & request)841 QModbusResponse QModbusServerPrivate::processDiagnosticsRequest(const QModbusRequest &request)
842 {
843 #define CHECK_SIZE_AND_CONDITION(req, condition) \
844     CHECK_SIZE_EQUALS(req); \
845     do { \
846         if ((condition)) { \
847             return QModbusExceptionResponse(req.functionCode(), \
848                                             QModbusExceptionResponse::IllegalDataValue); \
849         } \
850     } while (0)
851 
852     quint16 subFunctionCode, data = 0xffff;
853     request.decodeData(&subFunctionCode, &data);
854 
855     switch (subFunctionCode) {
856     case Diagnostics::ReturnQueryData:
857         return QModbusResponse(request.functionCode(), request.data());
858 
859     case Diagnostics::RestartCommunicationsOption: {
860         CHECK_SIZE_AND_CONDITION(request, ((data != 0xff00) && (data != 0x0000)));
861         // Restarts the communication by closing the connection and re-opening. After closing,
862         // all communication counters are cleared and the listen only mode set to false. This
863         // function is the only way to remotely clear the listen only mode and bring the device
864         // back into communication. If data is 0xff00, the event log history is also cleared.
865         q_func()->disconnectDevice();
866         if (data == 0xff00)
867             m_commEventLog.clear();
868 
869         resetCommunicationCounters();
870         q_func()->setValue(QModbusServer::ListenOnlyMode, false);
871         storeModbusCommEvent(QModbusCommEvent::InitiatedCommunicationRestart);
872 
873         if (!q_func()->connectDevice()) {
874             qCWarning(QT_MODBUS) << "(Server) Cannot restart server communication";
875             return QModbusExceptionResponse(request.functionCode(),
876                                             QModbusExceptionResponse::ServerDeviceFailure);
877         }
878         return QModbusResponse(request.functionCode(), request.data());
879     }   break;
880 
881     case Diagnostics::ChangeAsciiInputDelimiter: {
882         const QByteArray data = request.data().mid(2, 2);
883         CHECK_SIZE_AND_CONDITION(request, (data[1] != 0x00));
884         q_func()->setValue(QModbusServer::AsciiInputDelimiter, data[0]);
885         return QModbusResponse(request.functionCode(), request.data());
886     }   break;
887 
888     case Diagnostics::ForceListenOnlyMode:
889         CHECK_SIZE_AND_CONDITION(request, (data != 0x0000));
890         q_func()->setValue(QModbusServer::ListenOnlyMode, true);
891         storeModbusCommEvent(QModbusCommEvent::EnteredListenOnlyMode);
892         return QModbusResponse();
893 
894     case Diagnostics::ClearCountersAndDiagnosticRegister:
895         CHECK_SIZE_AND_CONDITION(request, (data != 0x0000));
896         resetCommunicationCounters();
897         q_func()->setValue(QModbusServer::DiagnosticRegister, 0x0000);
898         return QModbusResponse(request.functionCode(), request.data());
899 
900     case Diagnostics::ReturnDiagnosticRegister:
901     case Diagnostics::ReturnBusMessageCount:
902     case Diagnostics::ReturnBusCommunicationErrorCount:
903     case Diagnostics::ReturnBusExceptionErrorCount:
904     case Diagnostics::ReturnServerMessageCount:
905     case Diagnostics::ReturnServerNoResponseCount:
906     case Diagnostics::ReturnServerNAKCount:
907     case Diagnostics::ReturnServerBusyCount:
908     case Diagnostics::ReturnBusCharacterOverrunCount:
909         CHECK_SIZE_AND_CONDITION(request, (data != 0x0000));
910         return QModbusResponse(request.functionCode(), subFunctionCode,
911                                m_counters[static_cast<Counter> (subFunctionCode)]);
912 
913     case Diagnostics::ClearOverrunCounterAndFlag: {
914         CHECK_SIZE_AND_CONDITION(request, (data != 0x0000));
915         m_counters[Diagnostics::ReturnBusCharacterOverrunCount] = 0;
916         quint16 reg = q_func()->value(QModbusServer::DiagnosticRegister).value<quint16>();
917         q_func()->setValue(QModbusServer::DiagnosticRegister, reg &~ 1); // clear first bit
918         return QModbusResponse(request.functionCode(), request.data());
919     }
920     }
921     return QModbusExceptionResponse(request.functionCode(),
922         QModbusExceptionResponse::IllegalFunction);
923 
924 #undef CHECK_SIZE_AND_CONDITION
925 }
926 
processGetCommEventCounterRequest(const QModbusRequest & request)927 QModbusResponse QModbusServerPrivate::processGetCommEventCounterRequest(const QModbusRequest &request)
928 {
929     CHECK_SIZE_EQUALS(request);
930     const QVariant tmp = q_func()->value(QModbusServer::DeviceBusy);
931     if (tmp.isNull() || (!tmp.isValid())) {
932         return QModbusExceptionResponse(request.functionCode(),
933             QModbusExceptionResponse::ServerDeviceFailure);
934     }
935     const quint16 deviceBusy = tmp.value<quint16>();
936     return QModbusResponse(request.functionCode(), deviceBusy, m_counters[Counter::CommEvent]);
937 }
938 
processGetCommEventLogRequest(const QModbusRequest & request)939 QModbusResponse QModbusServerPrivate::processGetCommEventLogRequest(const QModbusRequest &request)
940 {
941     CHECK_SIZE_EQUALS(request);
942     const QVariant tmp = q_func()->value(QModbusServer::DeviceBusy);
943     if (tmp.isNull() || (!tmp.isValid())) {
944         return QModbusExceptionResponse(request.functionCode(),
945             QModbusExceptionResponse::ServerDeviceFailure);
946     }
947     const quint16 deviceBusy = tmp.value<quint16>();
948 
949     QVector<quint8> eventLog(int(m_commEventLog.size()));
950     std::copy(m_commEventLog.cbegin(), m_commEventLog.cend(), eventLog.begin());
951 
952     // 6 -> 3 x 2 Bytes (Status, Event Count and Message Count)
953     return QModbusResponse(request.functionCode(), quint8(eventLog.size() + 6), deviceBusy,
954         m_counters[Counter::CommEvent], m_counters[Counter::BusMessage], eventLog);
955 }
956 
processWriteMultipleCoilsRequest(const QModbusRequest & request)957 QModbusResponse QModbusServerPrivate::processWriteMultipleCoilsRequest(const QModbusRequest &request)
958 {
959     CHECK_SIZE_LESS_THAN(request);
960     quint16 address, numberOfCoils;
961     quint8 byteCount;
962     request.decodeData(&address, &numberOfCoils, &byteCount);
963 
964     // byte count does not match number of data bytes following
965     if (byteCount != (request.dataSize() - 5 )) {
966         return QModbusExceptionResponse(request.functionCode(),
967             QModbusExceptionResponse::IllegalDataValue);
968     }
969 
970     quint8 expectedBytes = numberOfCoils / 8;
971     if ((numberOfCoils % 8) != 0)
972         expectedBytes += 1;
973 
974     if ((numberOfCoils < 0x0001) || (numberOfCoils > 0x07B0) || (expectedBytes != byteCount)) {
975         return QModbusExceptionResponse(request.functionCode(),
976             QModbusExceptionResponse::IllegalDataValue);
977     }
978 
979     // Get the requested range out of the registers.
980     QModbusDataUnit coils(QModbusDataUnit::Coils, address, numberOfCoils);
981     if (!q_func()->data(&coils)) {
982         return QModbusExceptionResponse(request.functionCode(),
983             QModbusExceptionResponse::IllegalDataAddress);
984     }
985 
986     QVector<quint8> bytes;
987     const QByteArray payload = request.data().mid(5);
988     for (qint32 i = payload.size() - 1; i >= 0; --i)
989         bytes.append(quint8(payload[i]));
990 
991     // Since we picked the coils at start address, data
992     // range is numberOfCoils and therefore index too.
993     quint16 coil = numberOfCoils;
994     qint32 currentBit = 8 - ((byteCount * 8) - numberOfCoils);
995     for (quint8 currentByte : qAsConst(bytes)) {
996         for (currentBit -= 1; currentBit >= 0; --currentBit)
997             coils.setValue(--coil, currentByte & (1U << currentBit) ? 1 : 0);
998         currentBit = 8;
999     }
1000 
1001     if (!q_func()->setData(coils)) {
1002         return QModbusExceptionResponse(request.functionCode(),
1003             QModbusExceptionResponse::ServerDeviceFailure);
1004     }
1005 
1006     return QModbusResponse(request.functionCode(), address, numberOfCoils);
1007 }
1008 
processWriteMultipleRegistersRequest(const QModbusRequest & request)1009 QModbusResponse QModbusServerPrivate::processWriteMultipleRegistersRequest(
1010     const QModbusRequest &request)
1011 {
1012     CHECK_SIZE_LESS_THAN(request);
1013     quint16 address, numberOfRegisters;
1014     quint8 byteCount;
1015     request.decodeData(&address, &numberOfRegisters, &byteCount);
1016 
1017     // byte count does not match number of data bytes following or register count
1018     if ((byteCount != (request.dataSize() - 5 )) || (byteCount != (numberOfRegisters * 2))) {
1019         return QModbusExceptionResponse(request.functionCode(),
1020                                         QModbusExceptionResponse::IllegalDataValue);
1021     }
1022 
1023     if ((numberOfRegisters < 0x0001) || (numberOfRegisters > 0x007B)) {
1024         return QModbusExceptionResponse(request.functionCode(),
1025             QModbusExceptionResponse::IllegalDataValue);
1026     }
1027 
1028     // Get the requested range out of the registers.
1029     QModbusDataUnit registers(QModbusDataUnit::HoldingRegisters, address, numberOfRegisters);
1030     if (!q_func()->data(&registers)) {
1031         return QModbusExceptionResponse(request.functionCode(),
1032             QModbusExceptionResponse::IllegalDataAddress);
1033     }
1034 
1035     const QByteArray pduData = request.data().remove(0,5);
1036     QDataStream stream(pduData);
1037 
1038     QVector<quint16> values;
1039     quint16 tmp;
1040     for (int i = 0; i < numberOfRegisters; i++) {
1041         stream >> tmp;
1042         values.append(tmp);
1043     }
1044 
1045     registers.setValues(values);
1046 
1047     if (!q_func()->setData(registers)) {
1048         return QModbusExceptionResponse(request.functionCode(),
1049             QModbusExceptionResponse::ServerDeviceFailure);
1050     }
1051 
1052     return QModbusResponse(request.functionCode(), address, numberOfRegisters);
1053 }
1054 
processReportServerIdRequest(const QModbusRequest & request)1055 QModbusResponse QModbusServerPrivate::processReportServerIdRequest(const QModbusRequest &request)
1056 {
1057     CHECK_SIZE_EQUALS(request);
1058 
1059     Q_Q(QModbusServer);
1060 
1061     QByteArray data;
1062     QVariant tmp = q->value(QModbusServer::ServerIdentifier);
1063     if (tmp.isNull() || (!tmp.isValid())) {
1064         return QModbusExceptionResponse(request.functionCode(),
1065             QModbusExceptionResponse::ServerDeviceFailure);
1066     }
1067     data.append(tmp.value<quint8>());
1068 
1069     tmp = q->value(QModbusServer::RunIndicatorStatus);
1070     if (tmp.isNull() || (!tmp.isValid())) {
1071         return QModbusExceptionResponse(request.functionCode(),
1072             QModbusExceptionResponse::ServerDeviceFailure);
1073     }
1074     data.append(tmp.value<quint8>());
1075 
1076     tmp = q->value(QModbusServer::AdditionalData);
1077     if (!tmp.isNull() && tmp.isValid())
1078         data.append(tmp.toByteArray());
1079 
1080     data.prepend(data.size()); // byte count
1081     return QModbusResponse(request.functionCode(), data);
1082 }
1083 
processMaskWriteRegisterRequest(const QModbusRequest & request)1084 QModbusResponse QModbusServerPrivate::processMaskWriteRegisterRequest(const QModbusRequest &request)
1085 {
1086     CHECK_SIZE_EQUALS(request);
1087     quint16 address, andMask, orMask;
1088     request.decodeData(&address, &andMask, &orMask);
1089 
1090     quint16 reg;
1091     if (!q_func()->data(QModbusDataUnit::HoldingRegisters, address, &reg)) {
1092         return QModbusExceptionResponse(request.functionCode(),
1093             QModbusExceptionResponse::IllegalDataAddress);
1094     }
1095 
1096     const quint16 result = (reg & andMask) | (orMask & (~ andMask));
1097     if (!q_func()->setData(QModbusDataUnit::HoldingRegisters, address, result)) {
1098         return QModbusExceptionResponse(request.functionCode(),
1099             QModbusExceptionResponse::ServerDeviceFailure);
1100     }
1101     return QModbusResponse(request.functionCode(), request.data());
1102 }
1103 
processReadWriteMultipleRegistersRequest(const QModbusRequest & request)1104 QModbusResponse QModbusServerPrivate::processReadWriteMultipleRegistersRequest(
1105     const QModbusRequest &request)
1106 {
1107     CHECK_SIZE_LESS_THAN(request);
1108     quint16 readStartAddress, readQuantity, writeStartAddress, writeQuantity;
1109     quint8 byteCount;
1110     request.decodeData(&readStartAddress, &readQuantity,
1111                        &writeStartAddress, &writeQuantity, &byteCount);
1112 
1113     // byte count does not match number of data bytes following or register count
1114     if ((byteCount != (request.dataSize() - 9 )) || (byteCount != (writeQuantity * 2))) {
1115         return QModbusExceptionResponse(request.functionCode(),
1116                                         QModbusExceptionResponse::IllegalDataValue);
1117     }
1118 
1119     if ((readQuantity < 0x0001) || (readQuantity > 0x007B)
1120             || (writeQuantity < 0x0001) || (writeQuantity > 0x0079)) {
1121         return QModbusExceptionResponse(request.functionCode(),
1122             QModbusExceptionResponse::IllegalDataValue);
1123     }
1124 
1125     // According to spec, write operation is executed before the read operation
1126     // Get the requested range out of the registers.
1127     QModbusDataUnit writeRegisters(QModbusDataUnit::HoldingRegisters, writeStartAddress,
1128                                    writeQuantity);
1129     if (!q_func()->data(&writeRegisters)) {
1130         return QModbusExceptionResponse(request.functionCode(),
1131             QModbusExceptionResponse::IllegalDataAddress);
1132     }
1133 
1134     const QByteArray pduData = request.data().remove(0,9);
1135     QDataStream stream(pduData);
1136 
1137     QVector<quint16> values;
1138     quint16 tmp;
1139     for (int i = 0; i < writeQuantity; i++) {
1140         stream >> tmp;
1141         values.append(tmp);
1142     }
1143 
1144     writeRegisters.setValues(values);
1145 
1146     if (!q_func()->setData(writeRegisters)) {
1147         return QModbusExceptionResponse(request.functionCode(),
1148             QModbusExceptionResponse::ServerDeviceFailure);
1149     }
1150 
1151     // Get the requested range out of the registers.
1152     QModbusDataUnit readRegisters(QModbusDataUnit::HoldingRegisters, readStartAddress,
1153                                   readQuantity);
1154     if (!q_func()->data(&readRegisters)) {
1155         return QModbusExceptionResponse(request.functionCode(),
1156             QModbusExceptionResponse::IllegalDataAddress);
1157     }
1158 
1159     return QModbusResponse(request.functionCode(), quint8(readQuantity * 2),
1160                            readRegisters.values());
1161 }
1162 
processReadFifoQueueRequest(const QModbusRequest & request)1163 QModbusResponse QModbusServerPrivate::processReadFifoQueueRequest(const QModbusRequest &request)
1164 {
1165     CHECK_SIZE_LESS_THAN(request);
1166     quint16 address;
1167     request.decodeData(&address);
1168 
1169     quint16 fifoCount;
1170     if (!q_func()->data(QModbusDataUnit::HoldingRegisters, address, &fifoCount)) {
1171         return QModbusExceptionResponse(request.functionCode(),
1172             QModbusExceptionResponse::IllegalDataAddress);
1173     }
1174 
1175     if (fifoCount > 31u) {
1176         return QModbusExceptionResponse(request.functionCode(),
1177             QModbusExceptionResponse::IllegalDataValue);
1178     }
1179 
1180     QModbusDataUnit fifoRegisters(QModbusDataUnit::HoldingRegisters, address + 1u, fifoCount);
1181     if (!q_func()->data(&fifoRegisters)) {
1182         return QModbusExceptionResponse(request.functionCode(),
1183             QModbusExceptionResponse::IllegalDataAddress);
1184     }
1185 
1186     return QModbusResponse(request.functionCode(), quint16((fifoCount * 2) + 2u), fifoCount,
1187                            fifoRegisters.values());
1188 }
1189 
processEncapsulatedInterfaceTransportRequest(const QModbusRequest & request)1190 QModbusResponse QModbusServerPrivate::processEncapsulatedInterfaceTransportRequest(
1191                                                                      const QModbusRequest &request)
1192 {
1193     CHECK_SIZE_LESS_THAN(request);
1194     quint8 MEIType;
1195     request.decodeData(&MEIType);
1196 
1197     switch (MEIType) {
1198         case EncapsulatedInterfaceTransport::CanOpenGeneralReference:
1199             break;
1200         case EncapsulatedInterfaceTransport::ReadDeviceIdentification: {
1201             if (request.dataSize() != 3u) {
1202                 return QModbusExceptionResponse(request.functionCode(),
1203                     QModbusExceptionResponse::IllegalDataValue);
1204             }
1205 
1206             const QVariant tmp = q_func()->value(QModbusServer::DeviceIdentification);
1207             if (tmp.isNull() || (!tmp.isValid())) {
1208                 // TODO: Is this correct?
1209                 return QModbusExceptionResponse(request.functionCode(),
1210                     QModbusExceptionResponse::ServerDeviceFailure);
1211             }
1212 
1213             QModbusDeviceIdentification objectPool = tmp.value<QModbusDeviceIdentification>();
1214             if (!objectPool.isValid()) {
1215                 // TODO: Is this correct?
1216                 return QModbusExceptionResponse(request.functionCode(),
1217                     QModbusExceptionResponse::ServerDeviceFailure);
1218             }
1219 
1220             quint8 readDeviceIdCode, objectId;
1221             request.decodeData(&MEIType, &readDeviceIdCode, &objectId);
1222             if (!objectPool.contains(objectId)) {
1223                 // Individual access requires the object Id to be present, so we will always fail.
1224                 // For all other cases we will reevaluate object Id after we reset it as per spec.
1225                 objectId = QModbusDeviceIdentification::VendorNameObjectId;
1226                 if (readDeviceIdCode == QModbusDeviceIdentification::IndividualReadDeviceIdCode
1227                     || !objectPool.contains(objectId)) {
1228                     return QModbusExceptionResponse(request.functionCode(),
1229                         QModbusExceptionResponse::IllegalDataAddress);
1230                 }
1231             }
1232 
1233             auto payload = [MEIType, readDeviceIdCode, objectId, objectPool](int lastObjectId) {
1234                 // TODO: Take conformity level into account.
1235                 QByteArray payload(6, Qt::Uninitialized);
1236                 payload[0] = MEIType;
1237                 payload[1] = readDeviceIdCode;
1238                 payload[2] = quint8(objectPool.conformityLevel());
1239                 payload[3] = quint8(0x00); // no more follows
1240                 payload[4] = quint8(0x00); // next object id
1241                 payload[5] = quint8(0x00); // number of objects
1242 
1243                 const QList<int> objectIds = objectPool.objectIds();
1244                 for (int id : objectIds) {
1245                     if (id < objectId)
1246                         continue;
1247                     if (id > lastObjectId)
1248                         break;
1249                     const QByteArray object = objectPool.value(id);
1250                     QByteArray objectData(2, Qt::Uninitialized);
1251                     objectData[0] = id;
1252                     objectData[1] = quint8(object.size());
1253                     objectData += object;
1254                     if (payload.size() + objectData.size() > 253) {
1255                         payload[3] = char(0xff); // more follows
1256                         payload[4] = id; // next object id
1257                         break;
1258                     }
1259                     payload.append(objectData);
1260                     payload[5] = payload[5] + 1u; // number of objects
1261                 }
1262                 return payload;
1263             };
1264 
1265             switch (readDeviceIdCode) {
1266             case QModbusDeviceIdentification::BasicReadDeviceIdCode:
1267                 // TODO: How to handle a valid Id <> VendorName ... MajorMinorRevision
1268                 return QModbusResponse(request.functionCode(),
1269                     payload(QModbusDeviceIdentification::MajorMinorRevisionObjectId));
1270             case QModbusDeviceIdentification::RegularReadDeviceIdCode:
1271                 // TODO: How to handle a valid Id <> VendorUrl ... UserApplicationName
1272                 return QModbusResponse(request.functionCode(),
1273                     payload(QModbusDeviceIdentification::UserApplicationNameObjectId));
1274             case QModbusDeviceIdentification::ExtendedReadDeviceIdCode:
1275                 // TODO: How to handle a valid Id < ProductDependent
1276                 return QModbusResponse(request.functionCode(),
1277                     payload(QModbusDeviceIdentification::UndefinedObjectId));
1278             case QModbusDeviceIdentification::IndividualReadDeviceIdCode: {
1279                 // TODO: Take conformity level into account.
1280                 const QByteArray object = objectPool.value(objectId);
1281                 QByteArray header(8, Qt::Uninitialized);
1282                 header[0] = MEIType;
1283                 header[1] = readDeviceIdCode;
1284                 header[2] = quint8(objectPool.conformityLevel());
1285                 header[3] = quint8(0x00); // no more follows
1286                 header[4] = quint8(0x00); // next object id
1287                 header[5] = quint8(0x01); // number of objects
1288                 header[6] = objectId;
1289                 header[7] = quint8(object.length());
1290                 return QModbusResponse(request.functionCode(), QByteArray(header + object));
1291             }
1292             default:
1293                 return QModbusExceptionResponse(request.functionCode(),
1294                     QModbusExceptionResponse::IllegalDataValue);
1295             }
1296         }   break;
1297     }
1298     return QModbusExceptionResponse(request.functionCode(),
1299         QModbusExceptionResponse::IllegalFunction);
1300 }
1301 
storeModbusCommEvent(const QModbusCommEvent & eventByte)1302 void QModbusServerPrivate::storeModbusCommEvent(const QModbusCommEvent &eventByte)
1303 {
1304     // Inserts an event byte at the start of the event log. If the event log
1305     // is already full, the byte at the end of the log will be removed. The
1306     // event log size is 64 bytes, starting at index 0.
1307     m_commEventLog.push_front(eventByte);
1308     if (m_commEventLog.size() > 64)
1309         m_commEventLog.pop_back();
1310 }
1311 
1312 #undef CHECK_SIZE_EQUALS
1313 #undef CHECK_SIZE_LESS_THAN
1314 
1315 QT_END_NAMESPACE
1316