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

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