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
49QT_BEGIN_NAMESPACE
50
51Q_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*/
96QModbusServer::QModbusServer(QObject *parent) :
97 QModbusDevice(*new QModbusServerPrivate, parent)
98{
99}
100
101/*!
102 \internal
103*/
104QModbusServer::~QModbusServer()
105{
106}
107
108/*!
109 \internal
110*/
111QModbusServer::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*/
125bool 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*/
135void 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*/
146int 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*/
205QVariant QModbusServer::value(int option) const
206{
207 Q_D(const QModbusServer);
208
209 switch (option) {
210 case DiagnosticRegister:
211 return d->m_serverOptions.value(akey: option, adefaultValue: quint16(0x0000));
212 case ExceptionStatusOffset:
213 return d->m_serverOptions.value(akey: option, adefaultValue: quint16(0x0000));
214 case DeviceBusy:
215 return d->m_serverOptions.value(akey: option, adefaultValue: quint16(0x0000));
216 case AsciiInputDelimiter:
217 return d->m_serverOptions.value(akey: option, adefaultValue: '\n');
218 case ListenOnlyMode:
219 return d->m_serverOptions.value(akey: option, adefaultValue: false);
220 case ServerIdentifier:
221 return d->m_serverOptions.value(akey: option, adefaultValue: quint8(0x0a));
222 case RunIndicatorStatus:
223 return d->m_serverOptions.value(akey: option, adefaultValue: quint8(0xff));
224 case AdditionalData:
225 return d->m_serverOptions.value(akey: option, adefaultValue: QByteArray("Qt Modbus Server"));
226 case DeviceIdentification:
227 return d->m_serverOptions.value(akey: option, adefaultValue: QVariant());
228 };
229
230 if (option < UserOption)
231 return QVariant();
232
233 return d->m_serverOptions.value(akey: option, adefaultValue: 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*/
310bool 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(akey: option, avalue: 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(newData: &coils))
329 return false;
330 d->m_serverOptions.insert(akey: option, avalue: 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(akey: option, avalue: tmp);
339 return true;
340 }
341 case AsciiInputDelimiter: {
342 CHECK_INT_OR_UINT(newValue);
343 bool ok = false;
344 if (newValue.toUInt(ok: &ok) > 0xff || !ok)
345 return false;
346 d->m_serverOptions.insert(akey: option, avalue: newValue);
347 return true;
348 }
349 case ListenOnlyMode: {
350 if (newValue.type() != QVariant::Bool)
351 return false;
352 d->m_serverOptions.insert(akey: option, avalue: newValue);
353 return true;
354 }
355 case ServerIdentifier:
356 CHECK_INT_OR_UINT(newValue);
357 d->m_serverOptions.insert(akey: option, avalue: 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(akey: option, avalue: 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(akey: option, avalue: additionalData);
374 return true;
375 }
376 case DeviceIdentification:
377 if (!newValue.canConvert<QModbusDeviceIdentification>())
378 return false;
379 d->m_serverOptions.insert(akey: option, avalue: newValue);
380 return true;
381 default:
382 break;
383 };
384
385 if (option < UserOption)
386 return false;
387 d->m_serverOptions.insert(akey: option, avalue: 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*/
413bool QModbusServer::data(QModbusDataUnit::RegisterType table, quint16 address, quint16 *data) const
414{
415 QModbusDataUnit unit(table, address, 1u);
416 if (data && readData(newData: &unit)) {
417 *data = unit.value(index: 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*/
433bool 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*/
449bool QModbusServer::setData(QModbusDataUnit::RegisterType table, quint16 address, quint16 data)
450{
451 return writeData(unit: 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*/
465bool QModbusServer::setData(const QModbusDataUnit &newData)
466{
467 return writeData(unit: 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*/
482bool QModbusServer::writeData(const QModbusDataUnit &newData)
483{
484 Q_D(QModbusServer);
485 if (!d->m_modbusDataUnitMap.contains(akey: 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(index: i);
507 const int translatedIndex = newData.startAddress() - current.startAddress() + i;
508 changeRequired |= (current.value(index: translatedIndex) != newValue);
509 current.setValue(index: translatedIndex, newValue);
510 }
511
512 if (changeRequired)
513 emit dataWritten(table: newData.registerType(), address: newData.startAddress(), size: 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*/
528bool QModbusServer::readData(QModbusDataUnit *newData) const
529{
530 Q_D(const QModbusServer);
531
532 if ((!newData) || (!d->m_modbusDataUnitMap.contains(akey: newData->registerType())))
533 return false;
534
535 const QModbusDataUnit &current = d->m_modbusDataUnitMap.value(akey: 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(pos: newData->startAddress() - current.startAddress(), len: 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*/
599QModbusResponse 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*/
620QModbusResponse QModbusServer::processPrivateRequest(const QModbusPdu &request)
621{
622 return QModbusExceptionResponse(request.functionCode(),
623 QModbusExceptionResponse::IllegalFunction);
624}
625
626// -- QModbusServerPrivate
627
628bool QModbusServerPrivate::setMap(const QModbusDataUnitMap &map)
629{
630 m_modbusDataUnitMap = map;
631 return true;
632}
633
634QModbusResponse 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
699QModbusResponse QModbusServerPrivate::processReadCoilsRequest(const QModbusRequest &request)
700{
701 return readBits(request, unitType: QModbusDataUnit::Coils);
702}
703
704QModbusResponse QModbusServerPrivate::processReadDiscreteInputsRequest(const QModbusRequest &rqst)
705{
706 return readBits(request: rqst, unitType: QModbusDataUnit::DiscreteInputs);
707}
708
709QModbusResponse QModbusServerPrivate::readBits(const QModbusPdu &request,
710 QModbusDataUnit::RegisterType unitType)
711{
712 CHECK_SIZE_EQUALS(request);
713 quint16 address, count;
714 request.decodeData(newData: &address, newData: &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(newData: &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(i: address, val: unit.value(index: address));
741
742 QByteArray payload = QByteArray::fromRawData(bytes.bits(), size: byteCount);
743 payload.prepend(c: char(byteCount));
744 return QModbusResponse(request.functionCode(), payload);
745}
746
747QModbusResponse QModbusServerPrivate::processReadHoldingRegistersRequest(const QModbusRequest &rqst)
748{
749 return readBytes(request: rqst, unitType: QModbusDataUnit::HoldingRegisters);
750}
751
752QModbusResponse QModbusServerPrivate::processReadInputRegistersRequest(const QModbusRequest &rqst)
753{
754 return readBytes(request: rqst, unitType: QModbusDataUnit::InputRegisters);
755}
756
757QModbusResponse QModbusServerPrivate::readBytes(const QModbusPdu &request,
758 QModbusDataUnit::RegisterType unitType)
759{
760 CHECK_SIZE_EQUALS(request);
761 quint16 address, count;
762 request.decodeData(newData: &address, newData: &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(newData: &unit)) {
772 return QModbusExceptionResponse(request.functionCode(),
773 QModbusExceptionResponse::IllegalDataAddress);
774 }
775
776 return QModbusResponse(request.functionCode(), quint8(count * 2), unit.values());
777}
778
779QModbusResponse QModbusServerPrivate::processWriteSingleCoilRequest(const QModbusRequest &request)
780{
781 return writeSingle(request, unitType: QModbusDataUnit::Coils);
782}
783
784QModbusResponse QModbusServerPrivate::processWriteSingleRegisterRequest(const QModbusRequest &rqst)
785{
786 return writeSingle(request: rqst, unitType: QModbusDataUnit::HoldingRegisters);
787}
788
789QModbusResponse QModbusServerPrivate::writeSingle(const QModbusPdu &request,
790 QModbusDataUnit::RegisterType unitType)
791{
792 CHECK_SIZE_EQUALS(request);
793 quint16 address, value;
794 request.decodeData(newData: &address, newData: &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(table: unitType, address, data: &reg)) {
803 return QModbusExceptionResponse(request.functionCode(),
804 QModbusExceptionResponse::IllegalDataAddress);
805 }
806
807 if (!q_func()->setData(table: unitType, address, data: value)) {
808 return QModbusExceptionResponse(request.functionCode(),
809 QModbusExceptionResponse::ServerDeviceFailure);
810 }
811
812 return QModbusResponse(request.functionCode(), address, value);
813}
814
815QModbusResponse 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(option: 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(newData: &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(index: address++)) // The padding happens inside value().
836 byte |= (1U << currentBit);
837
838 return QModbusResponse(request.functionCode(), byte);
839}
840
841QModbusResponse 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(newData: &subFunctionCode, newData: &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(option: QModbusServer::ListenOnlyMode, newValue: false);
871 storeModbusCommEvent(eventByte: 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(index: 2, len: 2);
883 CHECK_SIZE_AND_CONDITION(request, (data[1] != 0x00));
884 q_func()->setValue(option: QModbusServer::AsciiInputDelimiter, newValue: 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(option: QModbusServer::ListenOnlyMode, newValue: true);
891 storeModbusCommEvent(eventByte: QModbusCommEvent::EnteredListenOnlyMode);
892 return QModbusResponse();
893
894 case Diagnostics::ClearCountersAndDiagnosticRegister:
895 CHECK_SIZE_AND_CONDITION(request, (data != 0x0000));
896 resetCommunicationCounters();
897 q_func()->setValue(option: QModbusServer::DiagnosticRegister, newValue: 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(option: QModbusServer::DiagnosticRegister).value<quint16>();
917 q_func()->setValue(option: QModbusServer::DiagnosticRegister, newValue: 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
927QModbusResponse QModbusServerPrivate::processGetCommEventCounterRequest(const QModbusRequest &request)
928{
929 CHECK_SIZE_EQUALS(request);
930 const QVariant tmp = q_func()->value(option: 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
939QModbusResponse QModbusServerPrivate::processGetCommEventLogRequest(const QModbusRequest &request)
940{
941 CHECK_SIZE_EQUALS(request);
942 const QVariant tmp = q_func()->value(option: 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(first: m_commEventLog.cbegin(), last: m_commEventLog.cend(), result: 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
957QModbusResponse QModbusServerPrivate::processWriteMultipleCoilsRequest(const QModbusRequest &request)
958{
959 CHECK_SIZE_LESS_THAN(request);
960 quint16 address, numberOfCoils;
961 quint8 byteCount;
962 request.decodeData(newData: &address, newData: &numberOfCoils, newData: &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(newData: &coils)) {
982 return QModbusExceptionResponse(request.functionCode(),
983 QModbusExceptionResponse::IllegalDataAddress);
984 }
985
986 QVector<quint8> bytes;
987 const QByteArray payload = request.data().mid(index: 5);
988 for (qint32 i = payload.size() - 1; i >= 0; --i)
989 bytes.append(t: 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(t&: bytes)) {
996 for (currentBit -= 1; currentBit >= 0; --currentBit)
997 coils.setValue(index: --coil, newValue: 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
1009QModbusResponse QModbusServerPrivate::processWriteMultipleRegistersRequest(
1010 const QModbusRequest &request)
1011{
1012 CHECK_SIZE_LESS_THAN(request);
1013 quint16 address, numberOfRegisters;
1014 quint8 byteCount;
1015 request.decodeData(newData: &address, newData: &numberOfRegisters, newData: &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(newData: &registers)) {
1031 return QModbusExceptionResponse(request.functionCode(),
1032 QModbusExceptionResponse::IllegalDataAddress);
1033 }
1034
1035 const QByteArray pduData = request.data().remove(index: 0,len: 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(t: 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
1055QModbusResponse 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(option: QModbusServer::ServerIdentifier);
1063 if (tmp.isNull() || (!tmp.isValid())) {
1064 return QModbusExceptionResponse(request.functionCode(),
1065 QModbusExceptionResponse::ServerDeviceFailure);
1066 }
1067 data.append(c: tmp.value<quint8>());
1068
1069 tmp = q->value(option: QModbusServer::RunIndicatorStatus);
1070 if (tmp.isNull() || (!tmp.isValid())) {
1071 return QModbusExceptionResponse(request.functionCode(),
1072 QModbusExceptionResponse::ServerDeviceFailure);
1073 }
1074 data.append(c: tmp.value<quint8>());
1075
1076 tmp = q->value(option: QModbusServer::AdditionalData);
1077 if (!tmp.isNull() && tmp.isValid())
1078 data.append(a: tmp.toByteArray());
1079
1080 data.prepend(c: data.size()); // byte count
1081 return QModbusResponse(request.functionCode(), data);
1082}
1083
1084QModbusResponse QModbusServerPrivate::processMaskWriteRegisterRequest(const QModbusRequest &request)
1085{
1086 CHECK_SIZE_EQUALS(request);
1087 quint16 address, andMask, orMask;
1088 request.decodeData(newData: &address, newData: &andMask, newData: &orMask);
1089
1090 quint16 reg;
1091 if (!q_func()->data(table: QModbusDataUnit::HoldingRegisters, address, data: &reg)) {
1092 return QModbusExceptionResponse(request.functionCode(),
1093 QModbusExceptionResponse::IllegalDataAddress);
1094 }
1095
1096 const quint16 result = (reg & andMask) | (orMask & (~ andMask));
1097 if (!q_func()->setData(table: QModbusDataUnit::HoldingRegisters, address, data: result)) {
1098 return QModbusExceptionResponse(request.functionCode(),
1099 QModbusExceptionResponse::ServerDeviceFailure);
1100 }
1101 return QModbusResponse(request.functionCode(), request.data());
1102}
1103
1104QModbusResponse 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(newData: &readStartAddress, newData: &readQuantity,
1111 newData: &writeStartAddress, newData: &writeQuantity, newData: &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(newData: &writeRegisters)) {
1130 return QModbusExceptionResponse(request.functionCode(),
1131 QModbusExceptionResponse::IllegalDataAddress);
1132 }
1133
1134 const QByteArray pduData = request.data().remove(index: 0,len: 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(t: 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(newData: &readRegisters)) {
1155 return QModbusExceptionResponse(request.functionCode(),
1156 QModbusExceptionResponse::IllegalDataAddress);
1157 }
1158
1159 return QModbusResponse(request.functionCode(), quint8(readQuantity * 2),
1160 readRegisters.values());
1161}
1162
1163QModbusResponse QModbusServerPrivate::processReadFifoQueueRequest(const QModbusRequest &request)
1164{
1165 CHECK_SIZE_LESS_THAN(request);
1166 quint16 address;
1167 request.decodeData(newData: &address);
1168
1169 quint16 fifoCount;
1170 if (!q_func()->data(table: QModbusDataUnit::HoldingRegisters, address, data: &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(newData: &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
1190QModbusResponse QModbusServerPrivate::processEncapsulatedInterfaceTransportRequest(
1191 const QModbusRequest &request)
1192{
1193 CHECK_SIZE_LESS_THAN(request);
1194 quint8 MEIType;
1195 request.decodeData(newData: &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(option: 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(newData: &MEIType, newData: &readDeviceIdCode, newData: &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(objectId: 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(a: 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
1302void 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(x: 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
1315QT_END_NAMESPACE
1316

source code of qtserialbus/src/serialbus/qmodbusserver.cpp