1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 The Qt Company Ltd. |
4 | ** Contact: http://www.qt.io/licensing/ |
5 | ** |
6 | ** This file is part of the QtSerialBus module of the Qt Toolkit. |
7 | ** |
8 | ** $QT_BEGIN_LICENSE:LGPL3$ |
9 | ** Commercial License Usage |
10 | ** Licensees holding valid commercial Qt licenses may use this file in |
11 | ** accordance with the commercial license agreement provided with the |
12 | ** Software or, alternatively, in accordance with the terms contained in |
13 | ** a written agreement between you and The Qt Company. For licensing terms |
14 | ** and conditions see http://www.qt.io/terms-conditions. For further |
15 | ** information use the contact form at http://www.qt.io/contact-us. |
16 | ** |
17 | ** GNU Lesser General Public License Usage |
18 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
19 | ** General Public License version 3 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPLv3 included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 3 requirements |
23 | ** will be met: https://www.gnu.org/licenses/lgpl.html. |
24 | ** |
25 | ** GNU General Public License Usage |
26 | ** Alternatively, this file may be used under the terms of the GNU |
27 | ** General Public License version 2.0 or later as published by the Free |
28 | ** Software Foundation and appearing in the file LICENSE.GPL included in |
29 | ** the packaging of this file. Please review the following information to |
30 | ** ensure the GNU General Public License version 2.0 requirements will be |
31 | ** met: http://www.gnu.org/licenses/gpl-2.0.html. |
32 | ** |
33 | ** $QT_END_LICENSE$ |
34 | ** |
35 | ****************************************************************************/ |
36 | |
37 | #include "qmodbusclient.h" |
38 | #include "qmodbusclient_p.h" |
39 | #include "qmodbus_symbols_p.h" |
40 | |
41 | #include <QtCore/qdebug.h> |
42 | #include <QtCore/qloggingcategory.h> |
43 | |
44 | QT_BEGIN_NAMESPACE |
45 | |
46 | Q_DECLARE_LOGGING_CATEGORY(QT_MODBUS) |
47 | |
48 | /*! |
49 | \class QModbusClient |
50 | \inmodule QtSerialBus |
51 | \since 5.8 |
52 | |
53 | \brief The QModbusClient class is the interface to send Modbus requests. |
54 | |
55 | The QModbusClient API is constructed around one QModbusClient object, which holds the common |
56 | configuration and settings for the requests it sends. One QModbusClient should be enough for |
57 | the whole Qt application. |
58 | |
59 | Once a QModbusClient object has been created, the application can use it to send requests. |
60 | The returned object is used to obtain any data returned in response to the corresponding request. |
61 | |
62 | QModbusClient has an asynchronous API. When the finished slot is called, the parameter |
63 | it takes is the QModbusReply object containing the PDU as well as meta-data (Addressing, etc.). |
64 | |
65 | Note: QModbusClient queues the requests it receives. The number of requests executed in |
66 | parallel is dependent on the protocol. For example, the HTTP protocol on desktop platforms |
67 | issues 6 requests in parallel for one host/port combination. |
68 | */ |
69 | |
70 | /*! |
71 | Constructs a Modbus client device with the specified \a parent. |
72 | */ |
73 | QModbusClient::QModbusClient(QObject *parent) |
74 | : QModbusDevice(*new QModbusClientPrivate, parent) |
75 | { |
76 | } |
77 | |
78 | /*! |
79 | \internal |
80 | */ |
81 | QModbusClient::~QModbusClient() |
82 | { |
83 | } |
84 | |
85 | /*! |
86 | Sends a request to read the contents of the data pointed by \a read. |
87 | Returns a new valid \l QModbusReply object if no error occurred, otherwise |
88 | nullptr. Modbus network may have multiple servers, each server has unique |
89 | \a serverAddress. |
90 | */ |
91 | QModbusReply *QModbusClient::sendReadRequest(const QModbusDataUnit &read, int serverAddress) |
92 | { |
93 | Q_D(QModbusClient); |
94 | return d->sendRequest(request: d->createReadRequest(data: read), serverAddress, unit: &read); |
95 | } |
96 | |
97 | /*! |
98 | Sends a request to modify the contents of the data pointed by \a write. |
99 | Returns a new valid \l QModbusReply object if no error occurred, otherwise |
100 | nullptr. Modbus network may have multiple servers, each server has unique |
101 | \a serverAddress. |
102 | */ |
103 | QModbusReply *QModbusClient::sendWriteRequest(const QModbusDataUnit &write, int serverAddress) |
104 | { |
105 | Q_D(QModbusClient); |
106 | return d->sendRequest(request: d->createWriteRequest(data: write), serverAddress, unit: &write); |
107 | } |
108 | |
109 | /*! |
110 | Sends a request to read the contents of the data pointed by \a read and to |
111 | modify the contents of the data pointed by \a write using Modbus function |
112 | code \l QModbusPdu::ReadWriteMultipleRegisters. |
113 | Returns a new valid \l QModbusReply object if no error occurred, otherwise |
114 | nullptr. Modbus network may have multiple servers, each server has unique |
115 | \a serverAddress. |
116 | |
117 | \note: Sending this kind of request is only valid of both \a read and |
118 | \a write are of type QModbusDataUnit::HoldingRegisters. |
119 | */ |
120 | QModbusReply *QModbusClient::sendReadWriteRequest(const QModbusDataUnit &read, |
121 | const QModbusDataUnit &write, int serverAddress) |
122 | { |
123 | Q_D(QModbusClient); |
124 | return d->sendRequest(request: d->createRWRequest(read, write), serverAddress, unit: &read); |
125 | } |
126 | |
127 | /*! |
128 | Sends a raw Modbus \a request. A raw request can contain anything that |
129 | fits inside the Modbus PDU data section and has a valid function code. |
130 | The only check performed before sending is therefore the validity check, |
131 | see \l QModbusPdu::isValid. If no error occurred the function returns a |
132 | a new valid \l QModbusReply; nullptr otherwise. Modbus networks may have |
133 | multiple servers, each server has a unique \a serverAddress. |
134 | |
135 | \sa QModbusReply::rawResult() |
136 | */ |
137 | QModbusReply *QModbusClient::sendRawRequest(const QModbusRequest &request, int serverAddress) |
138 | { |
139 | return d_func()->sendRequest(request, serverAddress, unit: nullptr); |
140 | } |
141 | |
142 | /*! |
143 | \property QModbusClient::timeout |
144 | \brief the timeout value used by this client |
145 | |
146 | Returns the timeout value used by this QModbusClient instance in ms. |
147 | A timeout is indicated by a \l TimeoutError. The default value is 1000 ms. |
148 | |
149 | \sa setTimeout |
150 | */ |
151 | int QModbusClient::timeout() const |
152 | { |
153 | Q_D(const QModbusClient); |
154 | return d->m_responseTimeoutDuration; |
155 | } |
156 | |
157 | /*! |
158 | \fn void QModbusClient::timeoutChanged(int newTimeout) |
159 | |
160 | This signal is emitted when the timeout used by this QModbusClient instance |
161 | is changed. The new response timeout for the device is passed as \a newTimeout. |
162 | |
163 | \sa setTimeout() |
164 | */ |
165 | |
166 | /*! |
167 | Sets the \a newTimeout for this QModbusClient instance. The minimum timeout |
168 | is 10 ms. |
169 | |
170 | The timeout is used by the client to determine how long it waits for |
171 | a response from the server. If the response is not received within the |
172 | required timeout, the \l TimeoutError is set. |
173 | |
174 | Already active/running timeouts are not affected by such timeout duration |
175 | changes. |
176 | |
177 | \sa timeout timeoutChanged() |
178 | */ |
179 | void QModbusClient::setTimeout(int newTimeout) |
180 | { |
181 | if (newTimeout < 10) |
182 | return; |
183 | |
184 | Q_D(QModbusClient); |
185 | if (d->m_responseTimeoutDuration != newTimeout) { |
186 | d->m_responseTimeoutDuration = newTimeout; |
187 | emit timeoutChanged(newTimeout); |
188 | } |
189 | } |
190 | |
191 | /*! |
192 | Returns the number of retries a client will perform before a |
193 | request fails. The default value is set to \c 3. |
194 | */ |
195 | int QModbusClient::numberOfRetries() const |
196 | { |
197 | Q_D(const QModbusClient); |
198 | return d->m_numberOfRetries; |
199 | } |
200 | |
201 | /*! |
202 | Sets the \a number of retries a client will perform before a |
203 | request fails. The default value is set to \c 3. |
204 | |
205 | \note The new value must be greater than or equal to \c 0. Changing this |
206 | property will only effect new requests, not already scheduled ones. |
207 | */ |
208 | void QModbusClient::setNumberOfRetries(int number) |
209 | { |
210 | Q_D(QModbusClient); |
211 | if (number >= 0) |
212 | d->m_numberOfRetries = number; |
213 | } |
214 | |
215 | /*! |
216 | \internal |
217 | */ |
218 | QModbusClient::QModbusClient(QModbusClientPrivate &dd, QObject *parent) : |
219 | QModbusDevice(dd, parent) |
220 | { |
221 | |
222 | } |
223 | |
224 | /*! |
225 | Processes a Modbus server \a response and stores the decoded information in \a data. Returns |
226 | true on success; otherwise false. |
227 | */ |
228 | bool QModbusClient::processResponse(const QModbusResponse &response, QModbusDataUnit *data) |
229 | { |
230 | return d_func()->processResponse(response, data); |
231 | } |
232 | |
233 | /*! |
234 | To be implemented by custom Modbus client implementation. The default implementation ignores |
235 | \a response and \a data. It always returns false to indicate error. |
236 | */ |
237 | bool QModbusClient::processPrivateResponse(const QModbusResponse &response, QModbusDataUnit *data) |
238 | { |
239 | Q_UNUSED(response) |
240 | Q_UNUSED(data) |
241 | return false; |
242 | } |
243 | |
244 | QModbusReply *QModbusClientPrivate::sendRequest(const QModbusRequest &request, int serverAddress, |
245 | const QModbusDataUnit *const unit) |
246 | { |
247 | Q_Q(QModbusClient); |
248 | |
249 | if (!isOpen() || q->state() != QModbusDevice::ConnectedState) { |
250 | qCWarning(QT_MODBUS) << "(Client) Device is not connected" ; |
251 | q->setError(errorText: QModbusClient::tr(s: "Device not connected." ), error: QModbusDevice::ConnectionError); |
252 | return nullptr; |
253 | } |
254 | |
255 | if (!request.isValid()) { |
256 | qCWarning(QT_MODBUS) << "(Client) Refuse to send invalid request." ; |
257 | q->setError(errorText: QModbusClient::tr(s: "Invalid Modbus request." ), error: QModbusDevice::ProtocolError); |
258 | return nullptr; |
259 | } |
260 | |
261 | if (unit) |
262 | return enqueueRequest(request, serverAddress, *unit, QModbusReply::Common); |
263 | return enqueueRequest(request, serverAddress, QModbusDataUnit(), QModbusReply::Raw); |
264 | } |
265 | |
266 | QModbusRequest QModbusClientPrivate::createReadRequest(const QModbusDataUnit &data) const |
267 | { |
268 | if (!data.isValid()) |
269 | return QModbusRequest(); |
270 | |
271 | switch (data.registerType()) { |
272 | case QModbusDataUnit::Coils: |
273 | return QModbusRequest(QModbusRequest::ReadCoils, quint16(data.startAddress()), |
274 | quint16(data.valueCount())); |
275 | case QModbusDataUnit::DiscreteInputs: |
276 | return QModbusRequest(QModbusRequest::ReadDiscreteInputs, quint16(data.startAddress()), |
277 | quint16(data.valueCount())); |
278 | case QModbusDataUnit::InputRegisters: |
279 | return QModbusRequest(QModbusRequest::ReadInputRegisters, quint16(data.startAddress()), |
280 | quint16(data.valueCount())); |
281 | case QModbusDataUnit::HoldingRegisters: |
282 | return QModbusRequest(QModbusRequest::ReadHoldingRegisters, quint16(data.startAddress()), |
283 | quint16(data.valueCount())); |
284 | default: |
285 | break; |
286 | } |
287 | |
288 | return QModbusRequest(); |
289 | } |
290 | |
291 | QModbusRequest QModbusClientPrivate::createWriteRequest(const QModbusDataUnit &data) const |
292 | { |
293 | switch (data.registerType()) { |
294 | case QModbusDataUnit::Coils: { |
295 | if (data.valueCount() == 1) { |
296 | return QModbusRequest(QModbusRequest::WriteSingleCoil, quint16(data.startAddress()), |
297 | quint16((data.value(index: 0) == 0u) ? Coil::Off : Coil::On)); |
298 | } |
299 | |
300 | quint8 byteCount = data.valueCount() / 8; |
301 | if ((data.valueCount() % 8) != 0) |
302 | byteCount += 1; |
303 | |
304 | quint8 address = 0; |
305 | QVector<quint8> bytes; |
306 | for (quint8 i = 0; i < byteCount; ++i) { |
307 | quint8 byte = 0; |
308 | for (int currentBit = 0; currentBit < 8; ++currentBit) |
309 | if (data.value(index: address++)) |
310 | byte |= (1U << currentBit); |
311 | bytes.append(t: byte); |
312 | } |
313 | |
314 | return QModbusRequest(QModbusRequest::WriteMultipleCoils, quint16(data.startAddress()), |
315 | quint16(data.valueCount()), byteCount, bytes); |
316 | } break; |
317 | |
318 | case QModbusDataUnit::HoldingRegisters: { |
319 | if (data.valueCount() == 1) { |
320 | return QModbusRequest(QModbusRequest::WriteSingleRegister, quint16(data.startAddress()), |
321 | data.value(index: 0)); |
322 | } |
323 | |
324 | const quint8 byteCount = data.valueCount() * 2; |
325 | return QModbusRequest(QModbusRequest::WriteMultipleRegisters, quint16(data.startAddress()), |
326 | quint16(data.valueCount()), byteCount, data.values()); |
327 | } break; |
328 | |
329 | case QModbusDataUnit::DiscreteInputs: |
330 | case QModbusDataUnit::InputRegisters: |
331 | default: // fall through on purpose |
332 | break; |
333 | } |
334 | return QModbusRequest(); |
335 | } |
336 | |
337 | QModbusRequest QModbusClientPrivate::createRWRequest(const QModbusDataUnit &read, |
338 | const QModbusDataUnit &write) const |
339 | { |
340 | if ((read.registerType() != QModbusDataUnit::HoldingRegisters) |
341 | && (write.registerType() != QModbusDataUnit::HoldingRegisters)) { |
342 | return QModbusRequest(); |
343 | } |
344 | |
345 | const quint8 byteCount = write.valueCount() * 2; |
346 | return QModbusRequest(QModbusRequest::ReadWriteMultipleRegisters, quint16(read.startAddress()), |
347 | quint16(read.valueCount()), quint16(write.startAddress()), |
348 | quint16(write.valueCount()), byteCount, write.values()); |
349 | } |
350 | |
351 | void QModbusClientPrivate::processQueueElement(const QModbusResponse &pdu, |
352 | const QueueElement &element) |
353 | { |
354 | if (element.reply.isNull()) |
355 | return; |
356 | |
357 | element.reply->setRawResult(pdu); |
358 | if (pdu.isException()) { |
359 | element.reply->setError(error: QModbusDevice::ProtocolError, |
360 | errorText: QModbusClient::tr(s: "Modbus Exception Response." )); |
361 | return; |
362 | } |
363 | |
364 | if (element.reply->type() == QModbusReply::Broadcast) { |
365 | element.reply->setFinished(true); |
366 | return; |
367 | } |
368 | |
369 | QModbusDataUnit unit = element.unit; |
370 | if (!processResponse(response: pdu, data: &unit)) { |
371 | element.reply->setError(error: QModbusDevice::UnknownError, |
372 | errorText: QModbusClient::tr(s: "An invalid response has been received." )); |
373 | return; |
374 | } |
375 | |
376 | element.reply->setResult(unit); |
377 | element.reply->setFinished(true); |
378 | } |
379 | |
380 | bool QModbusClientPrivate::processResponse(const QModbusResponse &response, QModbusDataUnit *data) |
381 | { |
382 | switch (response.functionCode()) { |
383 | case QModbusRequest::ReadCoils: |
384 | return processReadCoilsResponse(response, data); |
385 | case QModbusRequest::ReadDiscreteInputs: |
386 | return processReadDiscreteInputsResponse(response, data); |
387 | case QModbusRequest::ReadHoldingRegisters: |
388 | return processReadHoldingRegistersResponse(response, data); |
389 | case QModbusRequest::ReadInputRegisters: |
390 | return processReadInputRegistersResponse(response, data); |
391 | case QModbusRequest::WriteSingleCoil: |
392 | return processWriteSingleCoilResponse(response, data); |
393 | case QModbusRequest::WriteSingleRegister: |
394 | return processWriteSingleRegisterResponse(response, data); |
395 | case QModbusRequest::ReadExceptionStatus: |
396 | case QModbusRequest::Diagnostics: |
397 | case QModbusRequest::GetCommEventCounter: |
398 | case QModbusRequest::GetCommEventLog: |
399 | return false; // Return early, it's not a private response. |
400 | case QModbusRequest::WriteMultipleCoils: |
401 | return processWriteMultipleCoilsResponse(response, data); |
402 | case QModbusRequest::WriteMultipleRegisters: |
403 | return processWriteMultipleRegistersResponse(response, data); |
404 | case QModbusRequest::ReportServerId: |
405 | case QModbusRequest::ReadFileRecord: |
406 | case QModbusRequest::WriteFileRecord: |
407 | case QModbusRequest::MaskWriteRegister: |
408 | return false; // Return early, it's not a private response. |
409 | case QModbusRequest::ReadWriteMultipleRegisters: |
410 | return processReadWriteMultipleRegistersResponse(response, data); |
411 | case QModbusRequest::ReadFifoQueue: |
412 | case QModbusRequest::EncapsulatedInterfaceTransport: |
413 | return false; // Return early, it's not a private response. |
414 | default: |
415 | break; |
416 | } |
417 | return q_func()->processPrivateResponse(response, data); |
418 | } |
419 | |
420 | static bool isValid(const QModbusResponse &response, QModbusResponse::FunctionCode fc) |
421 | { |
422 | if (!response.isValid()) |
423 | return false; |
424 | if (response.isException()) |
425 | return false; |
426 | if (response.functionCode() != fc) |
427 | return false; |
428 | return true; |
429 | } |
430 | |
431 | bool QModbusClientPrivate::processReadCoilsResponse(const QModbusResponse &response, |
432 | QModbusDataUnit *data) |
433 | { |
434 | if (!isValid(response, fc: QModbusResponse::ReadCoils)) |
435 | return false; |
436 | return collateBits(pdu: response, type: QModbusDataUnit::Coils, data); |
437 | } |
438 | |
439 | bool QModbusClientPrivate::processReadDiscreteInputsResponse(const QModbusResponse &response, |
440 | QModbusDataUnit *data) |
441 | { |
442 | if (!isValid(response, fc: QModbusResponse::ReadDiscreteInputs)) |
443 | return false; |
444 | return collateBits(pdu: response, type: QModbusDataUnit::DiscreteInputs, data); |
445 | } |
446 | |
447 | bool QModbusClientPrivate::collateBits(const QModbusPdu &response, |
448 | QModbusDataUnit::RegisterType type, QModbusDataUnit *data) |
449 | { |
450 | if (response.dataSize() < QModbusResponse::minimumDataSize(pdu: response)) |
451 | return false; |
452 | |
453 | const QByteArray payload = response.data(); |
454 | // byte count needs to match available bytes |
455 | if ((payload.size() - 1) != payload[0]) |
456 | return false; |
457 | |
458 | if (data) { |
459 | uint value = 0; |
460 | for (qint32 i = 1; i < payload.size(); ++i) { |
461 | const quint8 byte = quint8(payload[i]); |
462 | for (qint32 currentBit = 0; currentBit < 8 && value < data->valueCount(); ++currentBit) |
463 | data->setValue(index: value++, newValue: byte & (1U << currentBit) ? 1 : 0); |
464 | } |
465 | data->setRegisterType(type); |
466 | } |
467 | return true; |
468 | } |
469 | |
470 | bool QModbusClientPrivate::processReadHoldingRegistersResponse(const QModbusResponse &response, |
471 | QModbusDataUnit *data) |
472 | { |
473 | if (!isValid(response, fc: QModbusResponse::ReadHoldingRegisters)) |
474 | return false; |
475 | return collateBytes(pdu: response, type: QModbusDataUnit::HoldingRegisters, data); |
476 | } |
477 | |
478 | bool QModbusClientPrivate::processReadInputRegistersResponse(const QModbusResponse &response, |
479 | QModbusDataUnit *data) |
480 | { |
481 | if (!isValid(response, fc: QModbusResponse::ReadInputRegisters)) |
482 | return false; |
483 | return collateBytes(pdu: response, type: QModbusDataUnit::InputRegisters, data); |
484 | } |
485 | |
486 | bool QModbusClientPrivate::collateBytes(const QModbusPdu &response, |
487 | QModbusDataUnit::RegisterType type, QModbusDataUnit *data) |
488 | { |
489 | if (response.dataSize() < QModbusResponse::minimumDataSize(pdu: response)) |
490 | return false; |
491 | |
492 | // byte count needs to match available bytes |
493 | const quint8 byteCount = quint8(response.data().at(i: 0)); |
494 | if ((response.dataSize() - 1) != byteCount) |
495 | return false; |
496 | |
497 | // byte count needs to be odd to match full registers |
498 | if (byteCount % 2 != 0) |
499 | return false; |
500 | |
501 | if (data) { |
502 | QDataStream stream(response.data().remove(index: 0, len: 1)); |
503 | |
504 | QVector<quint16> values; |
505 | const quint8 itemCount = byteCount / 2; |
506 | for (int i = 0; i < itemCount; i++) { |
507 | quint16 tmp; |
508 | stream >> tmp; |
509 | values.append(t: tmp); |
510 | } |
511 | data->setValues(values); |
512 | data->setRegisterType(type); |
513 | } |
514 | return true; |
515 | } |
516 | |
517 | bool QModbusClientPrivate::processWriteSingleCoilResponse(const QModbusResponse &response, |
518 | QModbusDataUnit *data) |
519 | { |
520 | if (!isValid(response, fc: QModbusResponse::WriteSingleCoil)) |
521 | return false; |
522 | return collateSingleValue(pdu: response, type: QModbusDataUnit::Coils, data); |
523 | } |
524 | |
525 | bool QModbusClientPrivate::processWriteSingleRegisterResponse(const QModbusResponse &response, |
526 | QModbusDataUnit *data) |
527 | { |
528 | if (!isValid(response, fc: QModbusResponse::WriteSingleRegister)) |
529 | return false; |
530 | return collateSingleValue(pdu: response, type: QModbusDataUnit::HoldingRegisters, data); |
531 | } |
532 | |
533 | bool QModbusClientPrivate::collateSingleValue(const QModbusPdu &response, |
534 | QModbusDataUnit::RegisterType type, QModbusDataUnit *data) |
535 | { |
536 | if (response.dataSize() != QModbusResponse::minimumDataSize(pdu: response)) |
537 | return false; |
538 | |
539 | quint16 address, value; |
540 | response.decodeData(newData: &address, newData: &value); |
541 | if ((type == QModbusDataUnit::Coils) && (value != Coil::Off) && (value != Coil::On)) |
542 | return false; |
543 | |
544 | if (data) { |
545 | data->setRegisterType(type); |
546 | data->setStartAddress(address); |
547 | data->setValues(QVector<quint16>{ value }); |
548 | } |
549 | return true; |
550 | } |
551 | |
552 | bool QModbusClientPrivate::processWriteMultipleCoilsResponse(const QModbusResponse &response, |
553 | QModbusDataUnit *data) |
554 | { |
555 | if (!isValid(response, fc: QModbusResponse::WriteMultipleCoils)) |
556 | return false; |
557 | return collateMultipleValues(pdu: response, type: QModbusDataUnit::Coils, data); |
558 | } |
559 | |
560 | bool QModbusClientPrivate::processWriteMultipleRegistersResponse(const QModbusResponse &response, |
561 | QModbusDataUnit *data) |
562 | { |
563 | if (!isValid(response, fc: QModbusResponse::WriteMultipleRegisters)) |
564 | return false; |
565 | return collateMultipleValues(pdu: response, type: QModbusDataUnit::HoldingRegisters, data); |
566 | } |
567 | |
568 | bool QModbusClientPrivate::collateMultipleValues(const QModbusPdu &response, |
569 | QModbusDataUnit::RegisterType type, QModbusDataUnit *data) |
570 | { |
571 | if (response.dataSize() != QModbusResponse::minimumDataSize(pdu: response)) |
572 | return false; |
573 | |
574 | quint16 address, count; |
575 | response.decodeData(newData: &address, newData: &count); |
576 | |
577 | // number of registers to write is 1-123 per request |
578 | if ((type == QModbusDataUnit::HoldingRegisters) && (count < 1 || count > 123)) |
579 | return false; |
580 | |
581 | if (data) { |
582 | data->setValueCount(count); |
583 | data->setRegisterType(type); |
584 | data->setStartAddress(address); |
585 | } |
586 | return true; |
587 | } |
588 | |
589 | bool QModbusClientPrivate::processReadWriteMultipleRegistersResponse(const QModbusResponse &resp, |
590 | QModbusDataUnit *data) |
591 | { |
592 | if (!isValid(response: resp, fc: QModbusResponse::ReadWriteMultipleRegisters)) |
593 | return false; |
594 | return collateBytes(response: resp, type: QModbusDataUnit::HoldingRegisters, data); |
595 | } |
596 | |
597 | QT_END_NAMESPACE |
598 | |