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

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