1 | // Copyright (C) 2018 basysKom GmbH, opensource@basyskom.com |
---|---|
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 "qopcuabinarydataencoding.h" |
5 | |
6 | #include <QtOpcUa/qopcuaapplicationrecorddatatype.h> |
7 | #include <QtOpcUa/qopcuaargument.h> |
8 | #include <QtOpcUa/qopcuaaxisinformation.h> |
9 | #include <QtOpcUa/qopcuacomplexnumber.h> |
10 | #include <QtOpcUa/qopcuadatavalue.h> |
11 | #include <QtOpcUa/qopcuadiagnosticinfo.h> |
12 | #include <QtOpcUa/qopcuadoublecomplexnumber.h> |
13 | #include <QtOpcUa/qopcuaenumdefinition.h> |
14 | #include <QtOpcUa/qopcuaenumfield.h> |
15 | #include <QtOpcUa/qopcuaeuinformation.h> |
16 | #include <QtOpcUa/qopcuaexpandednodeid.h> |
17 | #include <QtOpcUa/qopcuaextensionobject.h> |
18 | #include <QtOpcUa/qopcualocalizedtext.h> |
19 | #include <QtOpcUa/qopcuaqualifiedname.h> |
20 | #include <QtOpcUa/qopcuarange.h> |
21 | #include <QtOpcUa/qopcuastructuredefinition.h> |
22 | #include <QtOpcUa/qopcuastructurefield.h> |
23 | #include <QtOpcUa/qopcuaxvalue.h> |
24 | |
25 | #include <QtCore/qbytearray.h> |
26 | #include <QtCore/qdatetime.h> |
27 | #include <QtCore/qtimezone.h> |
28 | #include <QtCore/quuid.h> |
29 | |
30 | QT_BEGIN_NAMESPACE |
31 | |
32 | /*! |
33 | \class QOpcUaBinaryDataEncoding |
34 | \inmodule QtOpcUa |
35 | \brief QOpcUaBinaryDataEncoding is a partial implementation of the OPC UA binary data encoding described in OPC UA part 6. |
36 | |
37 | It offers template functions for encoding and decoding data for reading and writing extension objects. |
38 | |
39 | The following types are supported: |
40 | |
41 | \table |
42 | \header |
43 | \li Qt type |
44 | \li OPC UA type |
45 | \row |
46 | \li quint8 |
47 | \li uint8 |
48 | \row |
49 | \li qint8 |
50 | \li int8 |
51 | \row |
52 | \li quint16 |
53 | \li uint16 |
54 | \row |
55 | \li qint16 |
56 | \li int16 |
57 | \row |
58 | \li quint32 |
59 | \li uint32 |
60 | \row |
61 | \li qint32 |
62 | \li int32 |
63 | \row |
64 | \li quint64 |
65 | \li uint64 |
66 | \row |
67 | \li qint64 |
68 | \li int64 |
69 | \row |
70 | \li float |
71 | \li float |
72 | \row |
73 | \li double |
74 | \li double |
75 | \row |
76 | \li QString |
77 | \li String |
78 | \row |
79 | \li QOpcUaQualifiedName |
80 | \li QualifiedName |
81 | \row |
82 | \li QOpcUaLocalizedText |
83 | \li LocalizedText |
84 | \row |
85 | \li QOpcUaEUInformation |
86 | \li EUInformation |
87 | \row |
88 | \li QOpcUaRange |
89 | \li Range |
90 | \row |
91 | \li QOpcUaComplexNumber |
92 | \li ComplexNumber |
93 | \row |
94 | \li QOpcUaDoubleComplexNumber |
95 | \li DoubleComplexNumber |
96 | \row |
97 | \li QOpcUaAxisInformation |
98 | \li AxisInformation |
99 | \row |
100 | \li QOpcUaXValue |
101 | \li XV |
102 | \row |
103 | \li QUuid |
104 | \li GUID |
105 | \row |
106 | \li QString node id |
107 | \li NodeId |
108 | \row |
109 | \li QByteArray |
110 | \li ByteString |
111 | \row |
112 | \li QDateTime |
113 | \li DateTime |
114 | \row |
115 | \li QOpcUa::UaStatusCode |
116 | \li StatusCode |
117 | \row |
118 | \li QOpcUaExpandedNodeId |
119 | \li ExpandedNodeId |
120 | \row |
121 | \li QOpcUaExtensionObject |
122 | \li ExtensionObject |
123 | \row |
124 | \li QOpcUaArgument |
125 | \li Argument |
126 | \row |
127 | \li QOpcUaDiagnosticInfo (since Qt 6.7) |
128 | \li DiagnosticInfo |
129 | \row |
130 | \li QOpcUaApplicationRecordDataType |
131 | \li ApplicationRecordDataType |
132 | \row |
133 | \li QOpcUaStructureDefinition (since Qt 6.7) |
134 | \li StructureDefintion |
135 | \row |
136 | \li QOpcUaStructureField (since Qt 6.7) |
137 | \li StructureField |
138 | \row |
139 | \li QOpcUaEnumDefinition (since Qt 6.7) |
140 | \li EnumDefintion |
141 | \row |
142 | \li QOpcUaEnumField (since Qt 6.7) |
143 | \li EnumField |
144 | \row |
145 | \li QOpcUaVariant (since Qt 6.7) |
146 | \li Variant |
147 | \row |
148 | \li QOpcUaDataValue (since Qt 6.7) |
149 | \li DataValue |
150 | \endtable |
151 | */ |
152 | |
153 | /*! |
154 | \fn template<typename T, QOpcUa::Types OVERLAY> T QOpcUaBinaryDataEncoding::decode(bool &success) |
155 | |
156 | Decodes a scalar value of type T from the data buffer. |
157 | \a success is set to \c true if the decoding was successful, \c false if not. |
158 | |
159 | The decoded value is returned. If \a success is false, the returned value is invalid. |
160 | |
161 | \sa decodeArray() |
162 | */ |
163 | |
164 | /*! |
165 | \fn template<typename T, QOpcUa::Types OVERLAY> bool QOpcUaBinaryDataEncoding::encode(const T &src) |
166 | |
167 | Encodes \a src of type T and appends the encoded value to the data buffer. |
168 | Returns \c true if the value has been successfully encoded. |
169 | |
170 | \sa encodeArray() |
171 | */ |
172 | |
173 | /*! |
174 | \fn template<typename T, QOpcUa::Types OVERLAY> QList<T> QOpcUaBinaryDataEncoding::decodeArray(bool &success) |
175 | |
176 | Decodes an array of type T from the data buffer. |
177 | \a success is set to \c true if the decoding was successful, \c false if not. |
178 | |
179 | The decoded value is returned. If \a success is false, the returned value is invalid. |
180 | |
181 | \sa decode() |
182 | */ |
183 | |
184 | /*! |
185 | \fn template<typename T, QOpcUa::Types OVERLAY> bool QOpcUaBinaryDataEncoding::encodeArray(const QList<T> &src) |
186 | |
187 | Encodes all elements of type T in \a src and appends the encoded values to the data buffer. |
188 | |
189 | Returns \c true if the value has been successfully encoded. |
190 | |
191 | \sa encode() |
192 | */ |
193 | |
194 | /*! |
195 | Constructs a binary data encoding object for the data buffer \a buffer. |
196 | \a buffer must not be deleted as long as this binary data encoding object is used. |
197 | */ |
198 | QOpcUaBinaryDataEncoding::QOpcUaBinaryDataEncoding(QByteArray *buffer) |
199 | : m_data(buffer) |
200 | { |
201 | } |
202 | |
203 | /*! |
204 | Constructs a binary data encoding object using the encoded body of \a object as data buffer. |
205 | |
206 | \a object must not be deleted as long as this binary data encoding object is used. |
207 | */ |
208 | QOpcUaBinaryDataEncoding::QOpcUaBinaryDataEncoding(QOpcUaExtensionObject &object) |
209 | : m_data(&object.encodedBodyRef()) |
210 | { |
211 | } |
212 | |
213 | bool QOpcUaBinaryDataEncoding::enoughData(int requiredSize) |
214 | { |
215 | if (!m_data) |
216 | return false; |
217 | return (m_data->size() - m_offset) >= requiredSize; |
218 | } |
219 | |
220 | /*! |
221 | Returns the current offset in the data buffer. |
222 | */ |
223 | int QOpcUaBinaryDataEncoding::offset() const |
224 | { |
225 | return m_offset; |
226 | } |
227 | |
228 | /*! |
229 | Sets the current offset in the data buffer to \a offset. |
230 | The first byte in the buffer has the offset 0. |
231 | */ |
232 | void QOpcUaBinaryDataEncoding::setOffset(int offset) |
233 | { |
234 | m_offset = offset; |
235 | } |
236 | |
237 | /*! |
238 | Truncates the data buffer to the current \l offset(). |
239 | If the offset is behind the current buffer size, this method does nothing. |
240 | |
241 | This method can be used to roll back after an unsuccessful encode by setting |
242 | the old offset and calling truncateBufferToOffset(). |
243 | */ |
244 | void QOpcUaBinaryDataEncoding::truncateBufferToOffset() |
245 | { |
246 | if (!m_data) |
247 | return; |
248 | |
249 | if (m_offset < m_data->size() - 1) |
250 | m_data->truncate(pos: m_offset + 1); |
251 | } |
252 | |
253 | template<> |
254 | bool QOpcUaBinaryDataEncoding::decode<bool>(bool &success) |
255 | { |
256 | if (!m_data) { |
257 | success = false; |
258 | return success; |
259 | } |
260 | |
261 | if (enoughData(requiredSize: sizeof(quint8))) { |
262 | auto temp = *reinterpret_cast<const quint8 *>(m_data->constData() + m_offset); |
263 | m_offset += sizeof(temp); |
264 | success = true; |
265 | return temp != 0; |
266 | } else { |
267 | success = false; |
268 | return false; |
269 | } |
270 | } |
271 | |
272 | template<> |
273 | QString QOpcUaBinaryDataEncoding::decode<QString>(bool &success) |
274 | { |
275 | if (!m_data) { |
276 | success = false; |
277 | return QString(); |
278 | } |
279 | |
280 | const auto length = decode<qint32>(success); |
281 | |
282 | if (length > 0 && !enoughData(requiredSize: length)) { |
283 | success = false; |
284 | return QString(); |
285 | } |
286 | |
287 | if (length > 0) { |
288 | QString temp = QString::fromUtf8(utf8: reinterpret_cast<const char *>(m_data->constData() + m_offset), size: length); |
289 | m_offset += length; |
290 | success = true; |
291 | return temp; |
292 | } else if (length == 0) { // Empty string |
293 | success = true; |
294 | return QString::fromUtf8(utf8: ""); |
295 | } else if (length == -1) { // Null string |
296 | success = true; |
297 | return QString(); |
298 | } |
299 | |
300 | success = false; |
301 | return QString(); |
302 | } |
303 | |
304 | template <> |
305 | QOpcUaQualifiedName QOpcUaBinaryDataEncoding::decode<QOpcUaQualifiedName>(bool &success) |
306 | { |
307 | QOpcUaQualifiedName temp; |
308 | temp.setNamespaceIndex(decode<quint16>(success)); |
309 | if (!success) |
310 | return QOpcUaQualifiedName(); |
311 | |
312 | temp.setName(decode<QString>(success)); |
313 | if (!success) |
314 | return QOpcUaQualifiedName(); |
315 | |
316 | return temp; |
317 | } |
318 | |
319 | template <> |
320 | QOpcUaLocalizedText QOpcUaBinaryDataEncoding::decode<QOpcUaLocalizedText>(bool &success) |
321 | { |
322 | QOpcUaLocalizedText temp; |
323 | quint8 encodingMask = decode<quint8>(success); |
324 | if (!success) |
325 | return QOpcUaLocalizedText(); |
326 | |
327 | if (encodingMask & 0x01) { |
328 | temp.setLocale(decode<QString>(success)); |
329 | if (!success) |
330 | return QOpcUaLocalizedText(); |
331 | } |
332 | if (encodingMask & 0x02) { |
333 | temp.setText(decode<QString>(success)); |
334 | if (!success) |
335 | return QOpcUaLocalizedText(); |
336 | } |
337 | return temp; |
338 | } |
339 | |
340 | template <> |
341 | QOpcUaEUInformation QOpcUaBinaryDataEncoding::decode<QOpcUaEUInformation>(bool &success) |
342 | { |
343 | QOpcUaEUInformation temp; |
344 | |
345 | temp.setNamespaceUri(decode<QString>(success)); |
346 | if (!success) |
347 | return QOpcUaEUInformation(); |
348 | |
349 | temp.setUnitId(decode<qint32>(success)); |
350 | if (!success) |
351 | return QOpcUaEUInformation(); |
352 | |
353 | temp.setDisplayName(decode<QOpcUaLocalizedText>(success)); |
354 | if (!success) |
355 | return QOpcUaEUInformation(); |
356 | |
357 | temp.setDescription(decode<QOpcUaLocalizedText>(success)); |
358 | if (!success) |
359 | return QOpcUaEUInformation(); |
360 | |
361 | return temp; |
362 | } |
363 | |
364 | template <> |
365 | QOpcUaRange QOpcUaBinaryDataEncoding::decode<QOpcUaRange>(bool &success) |
366 | { |
367 | QOpcUaRange temp; |
368 | |
369 | temp.setLow(decode<double>(success)); |
370 | if (!success) |
371 | return QOpcUaRange(); |
372 | |
373 | temp.setHigh(decode<double>(success)); |
374 | if (!success) |
375 | return QOpcUaRange(); |
376 | |
377 | return temp; |
378 | } |
379 | |
380 | template <> |
381 | QOpcUaComplexNumber QOpcUaBinaryDataEncoding::decode<QOpcUaComplexNumber>(bool &success) |
382 | { |
383 | QOpcUaComplexNumber temp; |
384 | |
385 | temp.setReal(decode<float>(success)); |
386 | if (!success) |
387 | return QOpcUaComplexNumber(); |
388 | |
389 | temp.setImaginary(decode<float>(success)); |
390 | if (!success) |
391 | return QOpcUaComplexNumber(); |
392 | |
393 | return temp; |
394 | } |
395 | |
396 | template <> |
397 | QOpcUaDoubleComplexNumber QOpcUaBinaryDataEncoding::decode<QOpcUaDoubleComplexNumber>(bool &success) |
398 | { |
399 | QOpcUaDoubleComplexNumber temp; |
400 | |
401 | temp.setReal(decode<double>(success)); |
402 | if (!success) |
403 | return QOpcUaDoubleComplexNumber(); |
404 | |
405 | temp.setImaginary(decode<double>(success)); |
406 | if (!success) |
407 | return QOpcUaDoubleComplexNumber(); |
408 | |
409 | return temp; |
410 | } |
411 | |
412 | template <> |
413 | QOpcUaAxisInformation QOpcUaBinaryDataEncoding::decode<QOpcUaAxisInformation>(bool &success) |
414 | { |
415 | QOpcUaAxisInformation temp; |
416 | |
417 | temp.setEngineeringUnits(decode<QOpcUaEUInformation>(success)); |
418 | if (!success) |
419 | return QOpcUaAxisInformation(); |
420 | |
421 | temp.setEURange(decode<QOpcUaRange>(success)); |
422 | if (!success) |
423 | return QOpcUaAxisInformation(); |
424 | |
425 | temp.setTitle(decode<QOpcUaLocalizedText>(success)); |
426 | if (!success) |
427 | return QOpcUaAxisInformation(); |
428 | |
429 | temp.setAxisScaleType(static_cast<QOpcUa::AxisScale>(decode<quint32>(success))); |
430 | if (!success) |
431 | return QOpcUaAxisInformation(); |
432 | |
433 | temp.setAxisSteps(decodeArray<double>(success)); |
434 | if (!success) |
435 | return QOpcUaAxisInformation(); |
436 | |
437 | return temp; |
438 | } |
439 | |
440 | template <> |
441 | QOpcUaXValue QOpcUaBinaryDataEncoding::decode<QOpcUaXValue>(bool &success) |
442 | { |
443 | QOpcUaXValue temp; |
444 | |
445 | temp.setX(decode<double>(success)); |
446 | if (!success) |
447 | return QOpcUaXValue(); |
448 | |
449 | temp.setValue(decode<float>(success)); |
450 | if (!success) |
451 | return QOpcUaXValue(); |
452 | |
453 | return temp; |
454 | } |
455 | |
456 | template <> |
457 | QUuid QOpcUaBinaryDataEncoding::decode<QUuid>(bool &success) |
458 | { |
459 | if (!m_data) { |
460 | success = false; |
461 | return QUuid(); |
462 | } |
463 | |
464 | // An UUID is 16 bytes long |
465 | const size_t uuidSize = 16; |
466 | if (!enoughData(requiredSize: uuidSize)) { |
467 | success = false; |
468 | return QUuid(); |
469 | } |
470 | |
471 | const auto data1 = decode<quint32>(success); |
472 | if (!success) |
473 | return QUuid(); |
474 | |
475 | const auto data2 = decode<quint16>(success); |
476 | if (!success) |
477 | return QUuid(); |
478 | |
479 | const auto data3 = decode<quint16>(success); |
480 | if (!success) |
481 | return QUuid(); |
482 | |
483 | const auto data4 = QByteArray::fromRawData(data: m_data->constData() + m_offset, size: 8); |
484 | if (!success) |
485 | return QUuid(); |
486 | |
487 | m_offset += 8; |
488 | |
489 | const QUuid temp = QUuid(data1, data2, data3, data4[0], data4[1], data4[2], |
490 | data4[3], data4[4], data4[5], data4[6], data4[7]); |
491 | |
492 | success = true; |
493 | return temp; |
494 | } |
495 | |
496 | template <> |
497 | QByteArray QOpcUaBinaryDataEncoding::decode<QByteArray>(bool &success) |
498 | { |
499 | if (!m_data) { |
500 | success = false; |
501 | return QByteArray(); |
502 | } |
503 | |
504 | qint32 size = decode<qint32>(success); |
505 | if (!success) |
506 | return QByteArray(); |
507 | |
508 | if (size > 0 && enoughData(requiredSize: size)) { |
509 | const QByteArray temp(m_data->constData() + m_offset, size); |
510 | m_offset += size; |
511 | return temp; |
512 | } else if (size == 0) { |
513 | return QByteArray(""); |
514 | } else if (size == -1) { |
515 | return QByteArray(); |
516 | } |
517 | |
518 | success = false; |
519 | return QByteArray(); |
520 | } |
521 | |
522 | template <> |
523 | QString QOpcUaBinaryDataEncoding::decode<QString, QOpcUa::Types::NodeId>(bool &success) |
524 | { |
525 | quint8 identifierType = decode<quint8>(success); |
526 | if (!success) |
527 | return QString(); |
528 | |
529 | identifierType &= ~(0x40 | 0x80); // Remove expanded node id flags |
530 | |
531 | quint16 namespaceIndex; |
532 | |
533 | if (identifierType == 0x00) { |
534 | // encodingType 0x00 does not transfer the namespace index, it has to be zero |
535 | // OPC UA 1.05 Part 6, Chapter 5.2.2.9, Section "Two Byte NodeId Binary DataEncoding" |
536 | namespaceIndex = 0; |
537 | } else if (identifierType == 0x01){ |
538 | // encodingType 0x01 transfers only one byte namespace index, has to be in range 0-255 |
539 | // OPC UA 1.05 Part 6, Chapter 5.2.2.9, Section "Four Byte NodeId Binary DataEncoding" |
540 | namespaceIndex = decode<quint8>(success); |
541 | } else { |
542 | namespaceIndex = decode<quint16>(success); |
543 | } |
544 | |
545 | if (!success) |
546 | return QString(); |
547 | |
548 | switch (identifierType) { |
549 | case 0x00: { |
550 | quint8 identifier = decode<quint8>(success); |
551 | if (!success) |
552 | return QString(); |
553 | return QStringLiteral("ns=%1;i=%2").arg(a: namespaceIndex).arg(a: identifier); |
554 | } |
555 | case 0x01: { |
556 | quint16 identifier = decode<quint16>(success); |
557 | if (!success) |
558 | return QString(); |
559 | return QStringLiteral("ns=%1;i=%2").arg(a: namespaceIndex).arg(a: identifier); |
560 | } |
561 | case 0x02: { |
562 | quint32 identifier = decode<quint32>(success); |
563 | if (!success) |
564 | return QString(); |
565 | return QStringLiteral("ns=%1;i=%2").arg(a: namespaceIndex).arg(a: identifier); |
566 | } |
567 | case 0x03: { |
568 | QString identifier = decode<QString>(success); |
569 | if (!success) |
570 | return QString(); |
571 | return QStringLiteral("ns=%1;s=%2").arg(a: namespaceIndex).arg(a: identifier); |
572 | } |
573 | case 0x04: { |
574 | QUuid identifier = decode<QUuid>(success); |
575 | if (!success) |
576 | return QString(); |
577 | return QStringLiteral("ns=%1;g=%2").arg(a: namespaceIndex).arg(a: QStringView(identifier.toString()).mid(pos: 1, n: 36)); // Remove enclosing {...} |
578 | } |
579 | case 0x05: { |
580 | QByteArray identifier = decode<QByteArray>(success); |
581 | if (!success) |
582 | return QString(); |
583 | return QStringLiteral("ns=%1;b=%2").arg(a: namespaceIndex).arg(a: QString::fromLatin1(ba: identifier.toBase64().constData())); |
584 | } |
585 | } |
586 | |
587 | success = false; |
588 | return QString(); |
589 | } |
590 | |
591 | template <> |
592 | QOpcUaExpandedNodeId QOpcUaBinaryDataEncoding::decode<QOpcUaExpandedNodeId>(bool &success) |
593 | { |
594 | if (!m_data) { |
595 | success = false; |
596 | return QOpcUaExpandedNodeId(); |
597 | } |
598 | |
599 | // Don't decode the first byte, it is required for decode<QString, QOpcUa::Types::NodeId>() |
600 | if (!enoughData(requiredSize: sizeof(quint8))) { |
601 | success = false; |
602 | return QOpcUaExpandedNodeId(); |
603 | } |
604 | bool hasNamespaceUri = *(reinterpret_cast<const quint8 *>(m_data->constData() + m_offset)) & 0x80; |
605 | bool hasServerIndex = *(reinterpret_cast<const quint8 *>(m_data->constData() + m_offset)) & 0x40; |
606 | |
607 | QString nodeId = decode<QString, QOpcUa::Types::NodeId>(success); |
608 | if (!success) |
609 | return QOpcUaExpandedNodeId(); |
610 | |
611 | QString namespaceUri; |
612 | if (hasNamespaceUri) { |
613 | namespaceUri = decode<QString>(success); |
614 | if (!success) |
615 | return QOpcUaExpandedNodeId(); |
616 | } |
617 | |
618 | quint32 serverIndex = 0; |
619 | if (hasServerIndex) { |
620 | serverIndex = decode<quint32>(success); |
621 | if (!success) |
622 | return QOpcUaExpandedNodeId(); |
623 | } |
624 | |
625 | return QOpcUaExpandedNodeId(namespaceUri, nodeId, serverIndex); |
626 | } |
627 | |
628 | template <> |
629 | QDateTime QOpcUaBinaryDataEncoding::decode<QDateTime>(bool &success) |
630 | { |
631 | qint64 timestamp = decode<qint64>(success); |
632 | if (!success) |
633 | return QDateTime(); |
634 | |
635 | if (timestamp == 0 || timestamp == upperBound<qint64>()) |
636 | return QDateTime(); |
637 | |
638 | // OPC UA 1.05 part 6, 5.2.2.5 |
639 | const QDateTime epochStart(QDate(1601, 1, 1), QTime(0, 0), QTimeZone::UTC); |
640 | return epochStart.addMSecs(msecs: timestamp / 10000); |
641 | } |
642 | |
643 | template <> |
644 | QOpcUa::UaStatusCode QOpcUaBinaryDataEncoding::decode<QOpcUa::UaStatusCode>(bool &success) |
645 | { |
646 | quint32 value = decode<quint32>(success); |
647 | if (!success) |
648 | return QOpcUa::UaStatusCode(0); |
649 | |
650 | return QOpcUa::UaStatusCode(value); |
651 | } |
652 | |
653 | template <> |
654 | QOpcUaExtensionObject QOpcUaBinaryDataEncoding::decode<QOpcUaExtensionObject>(bool &success) |
655 | { |
656 | QOpcUaExtensionObject temp; |
657 | |
658 | QString typeId = decode<QString, QOpcUa::Types::NodeId>(success); |
659 | if (!success) |
660 | return QOpcUaExtensionObject(); |
661 | |
662 | temp.setEncodingTypeId(typeId); |
663 | quint8 encoding = decode<quint8>(success); |
664 | if (!success || encoding > 2) { |
665 | success = false; |
666 | return QOpcUaExtensionObject(); |
667 | } |
668 | temp.setEncoding(QOpcUaExtensionObject::Encoding(encoding)); |
669 | if (encoding == 0) |
670 | return temp; |
671 | |
672 | QByteArray body = decode<QByteArray>(success); |
673 | if (!success) |
674 | return QOpcUaExtensionObject(); |
675 | |
676 | temp.setEncodedBody(body); |
677 | return temp; |
678 | } |
679 | |
680 | template <> |
681 | QOpcUaArgument QOpcUaBinaryDataEncoding::decode<QOpcUaArgument>(bool &success) |
682 | { |
683 | QOpcUaArgument temp; |
684 | |
685 | temp.setName(decode<QString>(success)); |
686 | if (!success) |
687 | return QOpcUaArgument(); |
688 | |
689 | temp.setDataTypeId(decode<QString, QOpcUa::Types::NodeId>(success)); |
690 | if (!success) |
691 | return QOpcUaArgument(); |
692 | |
693 | temp.setValueRank(decode<qint32>(success)); |
694 | if (!success) |
695 | return QOpcUaArgument(); |
696 | |
697 | temp.setArrayDimensions(decodeArray<quint32>(success)); |
698 | if (!success) |
699 | return QOpcUaArgument(); |
700 | |
701 | temp.setDescription(decode<QOpcUaLocalizedText>(success)); |
702 | if (!success) |
703 | return QOpcUaArgument(); |
704 | |
705 | return temp; |
706 | } |
707 | |
708 | template <> |
709 | QOpcUaStructureField QOpcUaBinaryDataEncoding::decode<QOpcUaStructureField>(bool &success) |
710 | { |
711 | QOpcUaStructureField temp; |
712 | |
713 | temp.setName(decode<QString>(success)); |
714 | if (!success) |
715 | return QOpcUaStructureField(); |
716 | |
717 | temp.setDescription(decode<QOpcUaLocalizedText>(success)); |
718 | if (!success) |
719 | return QOpcUaStructureField(); |
720 | |
721 | temp.setDataType(decode<QString, QOpcUa::NodeId>(success)); |
722 | if (!success) |
723 | return QOpcUaStructureField(); |
724 | |
725 | temp.setValueRank(decode<qint32>(success)); |
726 | if (!success) |
727 | return QOpcUaStructureField(); |
728 | |
729 | temp.setArrayDimensions(decodeArray<quint32>(success)); |
730 | if (!success) |
731 | return QOpcUaStructureField(); |
732 | |
733 | temp.setMaxStringLength(decode<quint32>(success)); |
734 | if (!success) |
735 | return QOpcUaStructureField(); |
736 | |
737 | temp.setIsOptional(decode<bool>(success)); |
738 | if (!success) |
739 | return QOpcUaStructureField(); |
740 | |
741 | return temp; |
742 | } |
743 | |
744 | template <> |
745 | QOpcUaStructureDefinition QOpcUaBinaryDataEncoding::decode<QOpcUaStructureDefinition>(bool &success) |
746 | { |
747 | QOpcUaStructureDefinition temp; |
748 | |
749 | temp.setDefaultEncodingId(decode<QString, QOpcUa::NodeId>(success)); |
750 | if (!success) |
751 | return QOpcUaStructureDefinition(); |
752 | |
753 | temp.setBaseDataType(decode<QString, QOpcUa::NodeId>(success)); |
754 | if (!success) |
755 | return QOpcUaStructureDefinition(); |
756 | |
757 | temp.setStructureType(static_cast<QOpcUaStructureDefinition::StructureType>(decode<qint32>(success))); |
758 | if (!success) |
759 | return QOpcUaStructureDefinition(); |
760 | |
761 | temp.setFields(decodeArray<QOpcUaStructureField>(success)); |
762 | if (!success) |
763 | return QOpcUaStructureDefinition(); |
764 | |
765 | return temp; |
766 | } |
767 | |
768 | template <> |
769 | QOpcUaEnumField QOpcUaBinaryDataEncoding::decode<QOpcUaEnumField>(bool &success) |
770 | { |
771 | QOpcUaEnumField temp; |
772 | |
773 | temp.setValue(decode<qint64>(success)); |
774 | if (!success) |
775 | return QOpcUaEnumField(); |
776 | |
777 | temp.setDisplayName(decode<QOpcUaLocalizedText>(success)); |
778 | if (!success) |
779 | return QOpcUaEnumField(); |
780 | |
781 | temp.setDescription(decode<QOpcUaLocalizedText>(success)); |
782 | if (!success) |
783 | return QOpcUaEnumField(); |
784 | |
785 | temp.setName(decode<QString>(success)); |
786 | if (!success) |
787 | return QOpcUaEnumField(); |
788 | |
789 | return temp; |
790 | } |
791 | |
792 | template <> |
793 | QOpcUaEnumDefinition QOpcUaBinaryDataEncoding::decode<QOpcUaEnumDefinition>(bool &success) |
794 | { |
795 | QOpcUaEnumDefinition temp; |
796 | |
797 | temp.setFields(decodeArray<QOpcUaEnumField>(success)); |
798 | if (!success) |
799 | return QOpcUaEnumDefinition(); |
800 | |
801 | return temp; |
802 | } |
803 | |
804 | template <> |
805 | QOpcUaDiagnosticInfo QOpcUaBinaryDataEncoding::decode<QOpcUaDiagnosticInfo>(bool &success) { |
806 | QOpcUaDiagnosticInfo temp; |
807 | |
808 | const auto encodingMask = decode<quint8>(success); |
809 | if (!success) |
810 | return {}; |
811 | |
812 | temp.setHasSymbolicId(encodingMask & 0x01); |
813 | temp.setHasNamespaceUri(encodingMask & 0x02); |
814 | temp.setHasLocalizedText(encodingMask & 0x04); |
815 | temp.setHasLocale(encodingMask & 0x08); |
816 | temp.setHasAdditionalInfo(encodingMask & 0x10); |
817 | temp.setHasInnerStatusCode(encodingMask & 0x20); |
818 | temp.setHasInnerDiagnosticInfo(encodingMask & 0x40); |
819 | |
820 | if (temp.hasSymbolicId()) { |
821 | temp.setSymbolicId(decode<qint32>(success)); |
822 | if (!success) |
823 | return {}; |
824 | } |
825 | |
826 | if (temp.hasNamespaceUri()) { |
827 | temp.setNamespaceUri(decode<qint32>(success)); |
828 | if (!success) |
829 | return {}; |
830 | } |
831 | |
832 | if (temp.hasLocale()) { |
833 | temp.setLocale(decode<qint32>(success)); |
834 | if (!success) |
835 | return {}; |
836 | } |
837 | |
838 | if (temp.hasLocalizedText()) { |
839 | temp.setLocalizedText(decode<qint32>(success)); |
840 | if (!success) |
841 | return {}; |
842 | } |
843 | |
844 | if (temp.hasAdditionalInfo()) { |
845 | temp.setAdditionalInfo(decode<QString>(success)); |
846 | if (!success) |
847 | return {}; |
848 | } |
849 | |
850 | if (temp.hasInnerStatusCode()) { |
851 | temp.setInnerStatusCode(decode<QOpcUa::UaStatusCode>(success)); |
852 | if (!success) |
853 | return {}; |
854 | } |
855 | |
856 | if (temp.hasInnerDiagnosticInfo()) { |
857 | temp.setInnerDiagnosticInfo(decode<QOpcUaDiagnosticInfo>(success)); |
858 | if (!success) |
859 | return {}; |
860 | } |
861 | |
862 | return temp; |
863 | } |
864 | |
865 | template <> |
866 | QOpcUaVariant QOpcUaBinaryDataEncoding::decode<QOpcUaVariant>(bool &success); |
867 | |
868 | template <> |
869 | QOpcUaDataValue QOpcUaBinaryDataEncoding::decode<QOpcUaDataValue>(bool &success) { |
870 | QOpcUaDataValue temp; |
871 | |
872 | const auto encodingMask = decode<quint8>(success); |
873 | if (!success) |
874 | return {}; |
875 | |
876 | if (encodingMask & (1 << 0)) { |
877 | temp.setValue(decode<QOpcUaVariant>(success)); |
878 | if (!success) |
879 | return {}; |
880 | } |
881 | |
882 | if (encodingMask & (1 << 1)) { |
883 | temp.setStatusCode(decode<QOpcUa::UaStatusCode>(success)); |
884 | if (!success) |
885 | return {}; |
886 | } |
887 | |
888 | if (encodingMask & (1 << 2)) { |
889 | temp.setSourceTimestamp(decode<QDateTime>(success)); |
890 | if (!success) |
891 | return {}; |
892 | } |
893 | |
894 | if (encodingMask & (1 << 3)) { |
895 | temp.setSourcePicoseconds(decode<quint16>(success)); |
896 | if (!success) |
897 | return {}; |
898 | } |
899 | |
900 | if (encodingMask & (1 << 4)) { |
901 | temp.setServerTimestamp(decode<QDateTime>(success)); |
902 | if (!success) |
903 | return {}; |
904 | } |
905 | |
906 | if (encodingMask & (1 << 5)) { |
907 | temp.setServerPicoseconds(decode<quint16>(success)); |
908 | if (!success) |
909 | return {}; |
910 | } |
911 | |
912 | return temp; |
913 | } |
914 | |
915 | template <> |
916 | QOpcUaVariant QOpcUaBinaryDataEncoding::decode<QOpcUaVariant>(bool &success) { |
917 | QOpcUaVariant temp; |
918 | |
919 | const auto encodingMask = decode<quint8>(success); |
920 | if (!success) |
921 | return {}; |
922 | |
923 | const bool hasArrayDimensions = encodingMask & (1 << 6); |
924 | const bool isArray = encodingMask & (1 << 7); |
925 | const auto type = QOpcUaVariant::ValueType(encodingMask & 0x1F); |
926 | |
927 | // Decode value |
928 | |
929 | QVariant value; |
930 | |
931 | switch (type) { |
932 | case QOpcUaVariant::ValueType::Boolean: |
933 | value = decodeValueArrayOrScalar<bool>(isArray, success); |
934 | break; |
935 | case QOpcUaVariant::ValueType::SByte: |
936 | value = decodeValueArrayOrScalar<qint8>(isArray, success); |
937 | break; |
938 | case QOpcUaVariant::ValueType::Byte: |
939 | value = decodeValueArrayOrScalar<quint8>(isArray, success); |
940 | break; |
941 | case QOpcUaVariant::ValueType::Int16: |
942 | value = decodeValueArrayOrScalar<qint16>(isArray, success); |
943 | break; |
944 | case QOpcUaVariant::ValueType::UInt16: |
945 | value = decodeValueArrayOrScalar<quint16>(isArray, success); |
946 | break; |
947 | case QOpcUaVariant::ValueType::Int32: |
948 | value = decodeValueArrayOrScalar<qint32>(isArray, success); |
949 | break; |
950 | case QOpcUaVariant::ValueType::UInt32: |
951 | value = decodeValueArrayOrScalar<quint32>(isArray, success); |
952 | break; |
953 | case QOpcUaVariant::ValueType::Int64: |
954 | value = decodeValueArrayOrScalar<qint64>(isArray, success); |
955 | break; |
956 | case QOpcUaVariant::ValueType::UInt64: |
957 | value = decodeValueArrayOrScalar<quint64>(isArray, success); |
958 | break; |
959 | case QOpcUaVariant::ValueType::Float: |
960 | value = decodeValueArrayOrScalar<float>(isArray, success); |
961 | break; |
962 | case QOpcUaVariant::ValueType::Double: |
963 | value = decodeValueArrayOrScalar<double>(isArray, success); |
964 | break; |
965 | case QOpcUaVariant::ValueType::String: |
966 | value = decodeValueArrayOrScalar<QString>(isArray, success); |
967 | break; |
968 | case QOpcUaVariant::ValueType::DateTime: |
969 | value = decodeValueArrayOrScalar<QDateTime>(isArray, success); |
970 | break; |
971 | case QOpcUaVariant::ValueType::Guid: |
972 | value = decodeValueArrayOrScalar<QUuid>(isArray, success); |
973 | break; |
974 | case QOpcUaVariant::ValueType::ByteString: |
975 | value = decodeValueArrayOrScalar<QByteArray>(isArray, success); |
976 | break; |
977 | case QOpcUaVariant::ValueType::XmlElement: |
978 | value = decodeValueArrayOrScalar<QString>(isArray, success); |
979 | break; |
980 | case QOpcUaVariant::ValueType::NodeId: |
981 | value = decodeValueArrayOrScalar<QString, QOpcUa::Types::NodeId>(isArray, success); |
982 | break; |
983 | case QOpcUaVariant::ValueType::ExpandedNodeId: |
984 | value = decodeValueArrayOrScalar<QOpcUaExpandedNodeId>(isArray, success); |
985 | break; |
986 | case QOpcUaVariant::ValueType::StatusCode: |
987 | value = decodeValueArrayOrScalar<QOpcUa::UaStatusCode>(isArray, success); |
988 | break; |
989 | case QOpcUaVariant::ValueType::QualifiedName: |
990 | value = decodeValueArrayOrScalar<QOpcUaQualifiedName>(isArray, success); |
991 | break; |
992 | case QOpcUaVariant::ValueType::LocalizedText: |
993 | value = decodeValueArrayOrScalar<QOpcUaLocalizedText>(isArray, success); |
994 | break; |
995 | case QOpcUaVariant::ValueType::ExtensionObject: |
996 | value = decodeValueArrayOrScalar<QOpcUaExtensionObject>(isArray, success); |
997 | break; |
998 | case QOpcUaVariant::ValueType::DataValue: |
999 | value = decodeValueArrayOrScalar<QOpcUaDataValue>(isArray, success); |
1000 | break; |
1001 | case QOpcUaVariant::ValueType::Variant: |
1002 | if (!isArray) |
1003 | return {}; // Variant must not contain a scalar variant as value |
1004 | value = QVariant::fromValue(value: decodeArray<QOpcUaVariant>(success)); |
1005 | break; |
1006 | case QOpcUaVariant::ValueType::DiagnosticInfo: |
1007 | value = decodeValueArrayOrScalar<QOpcUaDiagnosticInfo>(isArray, success); |
1008 | break; |
1009 | default: |
1010 | break; |
1011 | } |
1012 | |
1013 | // 26-31 are reserved and shall be treated as ByteString when encountered |
1014 | // See OPC UA 1.05, Part 6, 5.2.2.16 |
1015 | auto resultType = type; |
1016 | if (static_cast<quint16>(type) >= 26 && static_cast<quint16>(type) <= 31) { |
1017 | value = decodeValueArrayOrScalar<QByteArray>(isArray, success); |
1018 | resultType = QOpcUaVariant::ValueType::ByteString; |
1019 | } |
1020 | |
1021 | if (!success) |
1022 | return {}; |
1023 | |
1024 | QList<qint32> arrayDimensions; |
1025 | if (hasArrayDimensions) |
1026 | arrayDimensions = decodeArray<qint32>(success); |
1027 | |
1028 | temp.setValue(type: resultType, value, arrayDimensions); |
1029 | |
1030 | return temp; |
1031 | } |
1032 | |
1033 | template<> |
1034 | bool QOpcUaBinaryDataEncoding::encode<bool>(const bool &src) |
1035 | { |
1036 | if (!m_data) |
1037 | return false; |
1038 | |
1039 | const quint8 value = src ? 1 : 0; |
1040 | m_data->append(s: reinterpret_cast<const char *>(&value), len: sizeof(value)); |
1041 | return true; |
1042 | } |
1043 | |
1044 | template<> |
1045 | bool QOpcUaBinaryDataEncoding::encode<QString>(const QString &src) |
1046 | { |
1047 | if (!m_data) |
1048 | return false; |
1049 | |
1050 | if (src.size() > upperBound<qint32>()) |
1051 | return false; |
1052 | |
1053 | QByteArray arr = src.toUtf8(); |
1054 | if (!encode<qint32>(src: arr.isNull() ? -1 : int(arr.size()))) |
1055 | return false; |
1056 | m_data->append(a: arr); |
1057 | return true; |
1058 | } |
1059 | |
1060 | template<> |
1061 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaQualifiedName>(const QOpcUaQualifiedName &src) |
1062 | { |
1063 | if (!encode<quint16>(src: src.namespaceIndex())) |
1064 | return false; |
1065 | if (!encode<QString>(src: src.name())) |
1066 | return false; |
1067 | return true; |
1068 | } |
1069 | |
1070 | template<> |
1071 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaLocalizedText>(const QOpcUaLocalizedText &src) |
1072 | { |
1073 | quint8 mask = 0; |
1074 | if (src.locale().size() != 0) |
1075 | mask |= 0x01; |
1076 | if (src.text().size() != 0) |
1077 | mask |= 0x02; |
1078 | if (!encode<quint8>(src: mask)) |
1079 | return false; |
1080 | if (src.locale().size()) |
1081 | if (!encode<QString>(src: src.locale())) |
1082 | return false; |
1083 | if (src.text().size()) |
1084 | if (!encode<QString>(src: src.text())) |
1085 | return false; |
1086 | return true; |
1087 | } |
1088 | |
1089 | template <> |
1090 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaRange>(const QOpcUaRange &src) |
1091 | { |
1092 | if (!encode<double>(src: src.low())) |
1093 | return false; |
1094 | if (!encode<double>(src: src.high())) |
1095 | return false; |
1096 | return true; |
1097 | } |
1098 | |
1099 | template <> |
1100 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaEUInformation>(const QOpcUaEUInformation &src) |
1101 | { |
1102 | if (!encode<QString>(src: src.namespaceUri())) |
1103 | return false; |
1104 | if (!encode<qint32>(src: src.unitId())) |
1105 | return false; |
1106 | if (!encode<QOpcUaLocalizedText>(src: src.displayName())) |
1107 | return false; |
1108 | if (!encode<QOpcUaLocalizedText>(src: src.description())) |
1109 | return false; |
1110 | return true; |
1111 | } |
1112 | |
1113 | template <> |
1114 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaComplexNumber>(const QOpcUaComplexNumber &src) |
1115 | { |
1116 | if (!encode<float>(src: src.real())) |
1117 | return false; |
1118 | if (!encode<float>(src: src.imaginary())) |
1119 | return false; |
1120 | return true; |
1121 | } |
1122 | |
1123 | template <> |
1124 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaDoubleComplexNumber>(const QOpcUaDoubleComplexNumber &src) |
1125 | { |
1126 | if (!encode<double>(src: src.real())) |
1127 | return false; |
1128 | if (!encode<double>(src: src.imaginary())) |
1129 | return false; |
1130 | return true; |
1131 | } |
1132 | |
1133 | template <> |
1134 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaAxisInformation>(const QOpcUaAxisInformation &src) |
1135 | { |
1136 | if (!encode<QOpcUaEUInformation>(src: src.engineeringUnits())) |
1137 | return false; |
1138 | if (!encode<QOpcUaRange>(src: src.eURange())) |
1139 | return false; |
1140 | if (!encode<QOpcUaLocalizedText>(src: src.title())) |
1141 | return false; |
1142 | if (!encode<quint32>(src: static_cast<quint32>(src.axisScaleType()))) |
1143 | return false; |
1144 | if (!encodeArray<double>(src: src.axisSteps())) |
1145 | return false; |
1146 | return true; |
1147 | } |
1148 | |
1149 | template <> |
1150 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaXValue>(const QOpcUaXValue &src) |
1151 | { |
1152 | if (!encode<double>(src: src.x())) |
1153 | return false; |
1154 | if (!encode<float>(src: src.value())) |
1155 | return false; |
1156 | return true; |
1157 | } |
1158 | |
1159 | template <> |
1160 | bool QOpcUaBinaryDataEncoding::encode<QUuid>(const QUuid &src) |
1161 | { |
1162 | if (!encode<quint32>(src: src.data1)) |
1163 | return false; |
1164 | if (!encode<quint16>(src: src.data2)) |
1165 | return false; |
1166 | if (!encode<quint16>(src: src.data3)) |
1167 | return false; |
1168 | |
1169 | auto data = QByteArray::fromRawData(data: reinterpret_cast<const char *>(src.data4), size: sizeof(src.data4)); |
1170 | m_data->append(a: data); |
1171 | |
1172 | return true; |
1173 | } |
1174 | |
1175 | template <> |
1176 | bool QOpcUaBinaryDataEncoding::encode<QByteArray>(const QByteArray &src) |
1177 | { |
1178 | if (!m_data) |
1179 | return false; |
1180 | |
1181 | if (src.size() > upperBound<qint32>()) |
1182 | return false; |
1183 | |
1184 | if (!encode<qint32>(src: src.isNull() ? -1 : int(src.size()))) |
1185 | return false; |
1186 | if (src.size() > 1) |
1187 | m_data->append(a: src); |
1188 | return true; |
1189 | } |
1190 | |
1191 | template <> |
1192 | bool QOpcUaBinaryDataEncoding::encode<QString, QOpcUa::Types::NodeId>(const QString &src) |
1193 | { |
1194 | if (!m_data) |
1195 | return false; |
1196 | |
1197 | quint16 index; |
1198 | QString identifier; |
1199 | char type; |
1200 | if (!QOpcUa::nodeIdStringSplit(nodeIdString: src.isEmpty() ? QStringLiteral("ns=0;i=0") : src, nsIndex: &index, identifier: &identifier, identifierType: &type)) |
1201 | return false; |
1202 | |
1203 | qint32 identifierType; |
1204 | switch (type) { |
1205 | case 'i': |
1206 | identifierType = 0; |
1207 | break; |
1208 | case 's': |
1209 | identifierType = 1; |
1210 | break; |
1211 | case 'g': |
1212 | identifierType = 2; |
1213 | break; |
1214 | case 'b': |
1215 | identifierType = 3; |
1216 | break; |
1217 | default: |
1218 | return false; |
1219 | } |
1220 | |
1221 | QByteArray encodedIdentifier; |
1222 | QOpcUaBinaryDataEncoding encoder(&encodedIdentifier); |
1223 | quint8 encodingType = 0; |
1224 | |
1225 | switch (identifierType) { |
1226 | case 0: { |
1227 | bool isNumber; |
1228 | uint integerIdentifier = identifier.toUInt(ok: &isNumber); |
1229 | if (!isNumber || integerIdentifier > upperBound<quint32>()) |
1230 | return false; |
1231 | |
1232 | if (integerIdentifier <= 255 && index == 0) { |
1233 | // encodingType 0x00 does not transfer the namespace index, it has to be zero |
1234 | // OPC UA 1.05 Part 6, Chapter 5.2.2.9, Section "Two Byte NodeId Binary DataEncoding" |
1235 | if (!encoder.encode<quint8>(src: integerIdentifier)) |
1236 | return false; |
1237 | encodingType = 0x00; // 8 bit numeric |
1238 | break; |
1239 | } else if (integerIdentifier <= 65535 && index <= 255) { |
1240 | // encodingType 0x01 transfers only one byte namespace index, has to be in range 0-255 |
1241 | // OPC UA 1.05 Part 6, Chapter 5.2.2.9, Section "Four Byte NodeId Binary DataEncoding" |
1242 | if (!encoder.encode<quint16>(src: integerIdentifier)) |
1243 | return false; |
1244 | encodingType = 0x01; // 16 bit numeric |
1245 | break; |
1246 | } else { |
1247 | if (!encoder.encode<quint32>(src: integerIdentifier)) |
1248 | return false; |
1249 | encodingType = 0x02; // 32 bit numeric |
1250 | break; |
1251 | } |
1252 | } |
1253 | case 1: { |
1254 | if (identifier.isEmpty()) |
1255 | return false; |
1256 | if (!encoder.encode<QString>(src: identifier)) |
1257 | return false; |
1258 | encodingType = 0x03; // String |
1259 | break; |
1260 | } |
1261 | case 2: { |
1262 | QUuid uuid(identifier); |
1263 | if (uuid.isNull()) |
1264 | return false; |
1265 | if (!encoder.encode<QUuid>(src: uuid)) |
1266 | return false; |
1267 | encodingType = 0x04; // GUID |
1268 | break; |
1269 | } |
1270 | case 3: { |
1271 | const QByteArray temp = QByteArray::fromBase64(base64: identifier.toLatin1()); |
1272 | if (temp.isEmpty()) |
1273 | return false; |
1274 | if (!encoder.encode<QByteArray>(src: temp)) |
1275 | return false; |
1276 | encodingType = 0x05; // ByteString |
1277 | break; |
1278 | } |
1279 | default: |
1280 | return false; |
1281 | } |
1282 | |
1283 | if (!encode<quint8>(src: encodingType)) |
1284 | return false; |
1285 | |
1286 | if (encodingType == 0x00) { |
1287 | // encodingType == 0x00 skips namespace completely, defaults to zero |
1288 | // OPC UA 1.05 Part 6, Chapter 5.2.2.9, Section "Two Byte NodeId Binary DataEncoding" |
1289 | } else if (encodingType == 0x01) { |
1290 | if (!encode<quint8>(src: index)) |
1291 | return false; |
1292 | } else { |
1293 | if (!encode<quint16>(src: index)) |
1294 | return false; |
1295 | } |
1296 | |
1297 | m_data->append(a: encodedIdentifier); |
1298 | return true; |
1299 | } |
1300 | |
1301 | template <> |
1302 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaExpandedNodeId>(const QOpcUaExpandedNodeId &src) |
1303 | { |
1304 | if (!m_data) |
1305 | return false; |
1306 | |
1307 | QByteArray temp; |
1308 | QOpcUaBinaryDataEncoding encoder(&temp); |
1309 | if (!encoder.encode<QString, QOpcUa::Types::NodeId>(src: src.nodeId())) |
1310 | return false; |
1311 | |
1312 | quint8 mask = temp.at(i: 0); |
1313 | |
1314 | if (!src.namespaceUri().isEmpty()) { |
1315 | mask |= 0x80; |
1316 | if (!encoder.encode<QString>(src: src.namespaceUri())) |
1317 | return false; |
1318 | } |
1319 | |
1320 | if (src.serverIndex() != 0) { |
1321 | mask |= 0x40; |
1322 | if (!encoder.encode<quint32>(src: src.serverIndex())) |
1323 | return false; |
1324 | } |
1325 | |
1326 | temp[0] = mask; |
1327 | |
1328 | m_data->append(a: temp); |
1329 | return true; |
1330 | } |
1331 | |
1332 | template <> |
1333 | bool QOpcUaBinaryDataEncoding::encode<QDateTime>(const QDateTime &src) |
1334 | { |
1335 | // OPC UA 1.05 part 6, 5.2.2.5 |
1336 | if (src >= QDateTime(QDate(9999, 12, 31), QTime(11, 59, 59))) { |
1337 | if (!encode<qint64>(src: upperBound<qint64>())) |
1338 | return false; |
1339 | return true; |
1340 | } |
1341 | |
1342 | const QDateTime uaEpochStart(QDate(1601, 1, 1), QTime(0, 0), QTimeZone::UTC); |
1343 | |
1344 | if (src <= uaEpochStart) { |
1345 | if (!encode<qint64>(src: 0)) |
1346 | return false; |
1347 | return true; |
1348 | } |
1349 | |
1350 | qint64 timestamp = 10000 * (src.toMSecsSinceEpoch() - uaEpochStart.toMSecsSinceEpoch()); |
1351 | if (!encode<qint64>(src: timestamp)) |
1352 | return false; |
1353 | return true; |
1354 | } |
1355 | |
1356 | template <> |
1357 | bool QOpcUaBinaryDataEncoding::encode<QOpcUa::UaStatusCode>(const QOpcUa::UaStatusCode &src) |
1358 | { |
1359 | if (!encode<quint32>(src)) |
1360 | return false; |
1361 | return true; |
1362 | } |
1363 | |
1364 | template <> |
1365 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaExtensionObject>(const QOpcUaExtensionObject &src) |
1366 | { |
1367 | if (!encode<QString, QOpcUa::Types::NodeId>(src: src.encodingTypeId())) |
1368 | return false; |
1369 | |
1370 | if (!encode<quint8>(src: quint8(src.encoding()))) |
1371 | return false; |
1372 | if (src.encoding() != QOpcUaExtensionObject::Encoding::NoBody) |
1373 | if (!encode<QByteArray>(src: src.encodedBody())) |
1374 | return false; |
1375 | |
1376 | return true; |
1377 | } |
1378 | |
1379 | template <> |
1380 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaArgument>(const QOpcUaArgument &src) |
1381 | { |
1382 | if (!m_data) |
1383 | return false; |
1384 | |
1385 | QByteArray temp; |
1386 | QOpcUaBinaryDataEncoding encoder(&temp); |
1387 | if (!encoder.encode<QString>(src: src.name())) |
1388 | return false; |
1389 | if (!encoder.encode<QString, QOpcUa::Types::NodeId>(src: src.dataTypeId())) |
1390 | return false; |
1391 | |
1392 | if (!encoder.encode<qint32>(src: src.valueRank())) |
1393 | return false; |
1394 | if (!encoder.encodeArray<quint32>(src: src.arrayDimensions())) |
1395 | return false; |
1396 | if (!encoder.encode<QOpcUaLocalizedText>(src: src.description())) |
1397 | return false; |
1398 | m_data->append(a: temp); |
1399 | |
1400 | return true; |
1401 | } |
1402 | |
1403 | template <> |
1404 | QOpcUaApplicationRecordDataType QOpcUaBinaryDataEncoding::decode<QOpcUaApplicationRecordDataType>(bool &success) |
1405 | { |
1406 | QOpcUaApplicationRecordDataType temp; |
1407 | |
1408 | temp.setApplicationId(decode<QString, QOpcUa::Types::NodeId>(success)); |
1409 | if (!success) |
1410 | return QOpcUaApplicationRecordDataType(); |
1411 | |
1412 | temp.setApplicationUri(decode<QString>(success)); |
1413 | if (!success) |
1414 | return QOpcUaApplicationRecordDataType(); |
1415 | |
1416 | temp.setApplicationType(static_cast<QOpcUaApplicationDescription::ApplicationType>(decode<uint32_t>(success))); |
1417 | if (!success) |
1418 | return QOpcUaApplicationRecordDataType(); |
1419 | |
1420 | temp.setApplicationNames(decodeArray<QOpcUaLocalizedText>(success)); |
1421 | if (!success) |
1422 | return QOpcUaApplicationRecordDataType(); |
1423 | |
1424 | temp.setProductUri(decode<QString>(success)); |
1425 | if (!success) |
1426 | return QOpcUaApplicationRecordDataType(); |
1427 | |
1428 | temp.setDiscoveryUrls(decodeArray<QString>(success)); |
1429 | if (!success) |
1430 | return QOpcUaApplicationRecordDataType(); |
1431 | |
1432 | temp.setServerCapabilityIdentifiers(decodeArray<QString>(success)); |
1433 | if (!success) |
1434 | return QOpcUaApplicationRecordDataType(); |
1435 | |
1436 | return temp; |
1437 | } |
1438 | |
1439 | template <> |
1440 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaApplicationRecordDataType>(const QOpcUaApplicationRecordDataType &src) |
1441 | { |
1442 | if (!encode<QString, QOpcUa::NodeId>(src: src.applicationId())) |
1443 | return false; |
1444 | if (!encode<QString>(src: src.applicationUri())) |
1445 | return false; |
1446 | if (!encode<uint32_t>(src: src.applicationType())) |
1447 | return false; |
1448 | if (!encodeArray<QOpcUaLocalizedText>(src: src.applicationNames())) |
1449 | return false; |
1450 | if (!encode<QString>(src: src.productUri())) |
1451 | return false; |
1452 | if (!encodeArray<QString>(src: src.discoveryUrls())) |
1453 | return false; |
1454 | if (!encodeArray<QString>(src: src.serverCapabilityIdentifiers())) |
1455 | return false; |
1456 | return true; |
1457 | } |
1458 | |
1459 | template <> |
1460 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaStructureField>(const QOpcUaStructureField &src) |
1461 | { |
1462 | if (!encode<QString>(src: src.name())) |
1463 | return false; |
1464 | if (!encode<QOpcUaLocalizedText>(src: src.description())) |
1465 | return false; |
1466 | if (!encode<QString, QOpcUa::NodeId>(src: src.dataType())) |
1467 | return false; |
1468 | if (!encode<qint32>(src: src.valueRank())) |
1469 | return false; |
1470 | if (!encodeArray<quint32>(src: src.arrayDimensions())) |
1471 | return false; |
1472 | if (!encode<quint32>(src: src.maxStringLength())) |
1473 | return false; |
1474 | if (!encode<bool>(src: src.isOptional())) |
1475 | return false; |
1476 | return true; |
1477 | } |
1478 | |
1479 | template <> |
1480 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaStructureDefinition>(const QOpcUaStructureDefinition &src) |
1481 | { |
1482 | if (!encode<QString, QOpcUa::NodeId>(src: src.defaultEncodingId())) |
1483 | return false; |
1484 | if (!encode<QString, QOpcUa::NodeId>(src: src.baseDataType())) |
1485 | return false; |
1486 | if (!encode<qint32>(src: static_cast<qint32>(src.structureType()))) |
1487 | return false; |
1488 | if (!encodeArray<QOpcUaStructureField>(src: src.fields())) |
1489 | return false; |
1490 | return true; |
1491 | } |
1492 | |
1493 | template <> |
1494 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaEnumField>(const QOpcUaEnumField &src) |
1495 | { |
1496 | if (!encode<qint64>(src: src.value())) |
1497 | return false; |
1498 | if (!encode<QOpcUaLocalizedText>(src: src.displayName())) |
1499 | return false; |
1500 | if (!encode<QOpcUaLocalizedText>(src: src.description())) |
1501 | return false; |
1502 | if (!encode<QString>(src: src.name())) |
1503 | return false; |
1504 | return true; |
1505 | } |
1506 | |
1507 | template <> |
1508 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaEnumDefinition>(const QOpcUaEnumDefinition &src) |
1509 | { |
1510 | if (!encodeArray<QOpcUaEnumField>(src: src.fields())) |
1511 | return false; |
1512 | return true; |
1513 | } |
1514 | |
1515 | template <> |
1516 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaDiagnosticInfo>(const QOpcUaDiagnosticInfo &src) |
1517 | { |
1518 | quint8 encodingMask = 0; |
1519 | if (src.hasSymbolicId()) |
1520 | encodingMask |= 0x01; |
1521 | if (src.hasNamespaceUri()) |
1522 | encodingMask |= 0x02; |
1523 | if (src.hasLocalizedText()) |
1524 | encodingMask |= 0x04; |
1525 | if (src.hasLocale()) |
1526 | encodingMask |= 0x08; |
1527 | if (src.hasAdditionalInfo()) |
1528 | encodingMask |= 0x10; |
1529 | if (src.hasInnerStatusCode()) |
1530 | encodingMask |= 0x20; |
1531 | if (src.hasInnerDiagnosticInfo()) |
1532 | encodingMask |= 0x40; |
1533 | |
1534 | if (!encode<quint8>(src: encodingMask)) |
1535 | return false; |
1536 | |
1537 | if (src.hasSymbolicId()) { |
1538 | if (!encode<qint32>(src: src.symbolicId())) |
1539 | return false; |
1540 | } |
1541 | |
1542 | if (src.hasNamespaceUri()) { |
1543 | if (!encode<qint32>(src: src.namespaceUri())) |
1544 | return false; |
1545 | } |
1546 | |
1547 | if (src.hasLocale()) { |
1548 | if (!encode<qint32>(src: src.locale())) |
1549 | return false; |
1550 | } |
1551 | |
1552 | if (src.hasLocalizedText()) { |
1553 | if (!encode<qint32>(src: src.localizedText())) |
1554 | return false; |
1555 | } |
1556 | |
1557 | if (src.hasAdditionalInfo()) { |
1558 | if (!encode<QString>(src: src.additionalInfo())) |
1559 | return false; |
1560 | } |
1561 | |
1562 | if (src.hasInnerStatusCode()) { |
1563 | if (!encode<QOpcUa::UaStatusCode>(src: src.innerStatusCode())) |
1564 | return false; |
1565 | } |
1566 | |
1567 | if (src.hasInnerDiagnosticInfo()) { |
1568 | if (!encode<QOpcUaDiagnosticInfo>(src: src.innerDiagnosticInfo())) |
1569 | return false; |
1570 | } |
1571 | |
1572 | return true; |
1573 | } |
1574 | |
1575 | template <> |
1576 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaDataValue>(const QOpcUaDataValue &src) |
1577 | { |
1578 | if (src.value().isValid() && !src.value().canConvert<QOpcUaVariant>()) { |
1579 | qWarning() << "Unable to convert DataValue value type != QOpcUaVariant"; |
1580 | return false; |
1581 | } |
1582 | |
1583 | quint8 encodingMask = 0; |
1584 | if (src.value().isValid()) |
1585 | encodingMask |= (1 << 0); |
1586 | if (src.statusCode() != QOpcUa::UaStatusCode::Good) |
1587 | encodingMask |= (1 << 1); |
1588 | if (src.sourceTimestamp().isValid()) |
1589 | encodingMask |= (1 << 2); |
1590 | if (src.serverTimestamp().isValid()) |
1591 | encodingMask |= (1 << 3); |
1592 | if (src.sourcePicoseconds()) |
1593 | encodingMask |= (1 << 4); |
1594 | if (src.serverPicoseconds()) |
1595 | encodingMask |= (1 << 5); |
1596 | |
1597 | if (!encode<quint8>(src: encodingMask)) |
1598 | return false; |
1599 | |
1600 | // Encode value |
1601 | if (src.value().isValid()) { |
1602 | if (!encode<QOpcUaVariant>(src: src.value().value<QOpcUaVariant>())) |
1603 | return false; |
1604 | } |
1605 | |
1606 | if (src.statusCode() != QOpcUa::UaStatusCode::Good) { |
1607 | if (!encode<QOpcUa::UaStatusCode>(src: src.statusCode())) |
1608 | return false; |
1609 | } |
1610 | |
1611 | if (src.sourceTimestamp().isValid()) { |
1612 | if (!encode<QDateTime>(src: src.sourceTimestamp())) |
1613 | return false; |
1614 | } |
1615 | |
1616 | if (src.sourcePicoseconds()) { |
1617 | if (!encode<quint16>(src: src.sourcePicoseconds())) |
1618 | return false; |
1619 | } |
1620 | |
1621 | if (src.serverTimestamp().isValid()) { |
1622 | if (!encode<QDateTime>(src: src.serverTimestamp())) |
1623 | return false; |
1624 | } |
1625 | |
1626 | if (src.serverPicoseconds()) { |
1627 | if (!encode<quint16>(src: src.serverPicoseconds())) |
1628 | return false; |
1629 | } |
1630 | |
1631 | return true; |
1632 | } |
1633 | |
1634 | template <> |
1635 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaVariant>(const QOpcUaVariant &src) |
1636 | { |
1637 | quint8 encodingMask = static_cast<quint8>(src.type()); |
1638 | if (!src.arrayDimensions().isEmpty()) |
1639 | encodingMask |= (1 << 6); |
1640 | if (src.isArray()) |
1641 | encodingMask |= (1 << 7); |
1642 | |
1643 | if (!encode<quint8>(src: encodingMask)) |
1644 | return false; |
1645 | |
1646 | bool success = true; |
1647 | switch (src.type()) { |
1648 | case QOpcUaVariant::ValueType::Boolean: |
1649 | success = encodeValueArrayOrScalar<bool>(var: src); |
1650 | break; |
1651 | case QOpcUaVariant::ValueType::SByte: |
1652 | success = encodeValueArrayOrScalar<qint8>(var: src); |
1653 | break; |
1654 | case QOpcUaVariant::ValueType::Byte: |
1655 | success = encodeValueArrayOrScalar<quint8>(var: src); |
1656 | break; |
1657 | case QOpcUaVariant::ValueType::Int16: |
1658 | success = encodeValueArrayOrScalar<qint16>(var: src); |
1659 | break; |
1660 | case QOpcUaVariant::ValueType::UInt16: |
1661 | success = encodeValueArrayOrScalar<quint16>(var: src); |
1662 | break; |
1663 | case QOpcUaVariant::ValueType::Int32: |
1664 | success = encodeValueArrayOrScalar<qint32>(var: src); |
1665 | break; |
1666 | case QOpcUaVariant::ValueType::UInt32: |
1667 | success = encodeValueArrayOrScalar<quint32>(var: src); |
1668 | break; |
1669 | case QOpcUaVariant::ValueType::Int64: |
1670 | success = encodeValueArrayOrScalar<qint64>(var: src); |
1671 | break; |
1672 | case QOpcUaVariant::ValueType::UInt64: |
1673 | success = encodeValueArrayOrScalar<quint32>(var: src); |
1674 | break; |
1675 | case QOpcUaVariant::ValueType::Float: |
1676 | success = encodeValueArrayOrScalar<float>(var: src); |
1677 | break; |
1678 | case QOpcUaVariant::ValueType::Double: |
1679 | success = encodeValueArrayOrScalar<double>(var: src); |
1680 | break; |
1681 | case QOpcUaVariant::ValueType::String: |
1682 | success = encodeValueArrayOrScalar<QString>(var: src); |
1683 | break; |
1684 | case QOpcUaVariant::ValueType::DateTime: |
1685 | success = encodeValueArrayOrScalar<QDateTime>(var: src); |
1686 | break; |
1687 | case QOpcUaVariant::ValueType::Guid: |
1688 | success = encodeValueArrayOrScalar<QUuid>(var: src); |
1689 | break; |
1690 | case QOpcUaVariant::ValueType::ByteString: |
1691 | success = encodeValueArrayOrScalar<QByteArray>(var: src); |
1692 | break; |
1693 | case QOpcUaVariant::ValueType::XmlElement: |
1694 | success = encodeValueArrayOrScalar<QString>(var: src); |
1695 | break; |
1696 | case QOpcUaVariant::ValueType::NodeId: |
1697 | success = encodeValueArrayOrScalar<QString, QOpcUa::Types::NodeId>(var: src); |
1698 | break; |
1699 | case QOpcUaVariant::ValueType::ExpandedNodeId: |
1700 | success = encodeValueArrayOrScalar<QOpcUaExpandedNodeId>(var: src); |
1701 | break; |
1702 | case QOpcUaVariant::ValueType::StatusCode: |
1703 | success = encodeValueArrayOrScalar<QOpcUa::UaStatusCode>(var: src); |
1704 | break; |
1705 | case QOpcUaVariant::ValueType::QualifiedName: |
1706 | success = encodeValueArrayOrScalar<QOpcUaQualifiedName>(var: src); |
1707 | break; |
1708 | case QOpcUaVariant::ValueType::LocalizedText: |
1709 | success = encodeValueArrayOrScalar<QOpcUaLocalizedText>(var: src); |
1710 | break; |
1711 | case QOpcUaVariant::ValueType::ExtensionObject: |
1712 | success = encodeValueArrayOrScalar<QOpcUaExtensionObject>(var: src); |
1713 | break; |
1714 | case QOpcUaVariant::ValueType::DataValue: |
1715 | success = encodeValueArrayOrScalar<QOpcUaDataValue>(var: src); |
1716 | break; |
1717 | case QOpcUaVariant::ValueType::Variant: |
1718 | if (!src.isArray()) { |
1719 | qWarning() << "Unable to convert Variant value, a Variant must not contain a scalar variant"; |
1720 | return false; |
1721 | } |
1722 | success = encode<QOpcUaVariant>(src); |
1723 | break; |
1724 | case QOpcUaVariant::ValueType::DiagnosticInfo: |
1725 | success = encodeValueArrayOrScalar<QOpcUaDiagnosticInfo>(var: src); |
1726 | break; |
1727 | default: |
1728 | break; |
1729 | } |
1730 | |
1731 | if (!success) |
1732 | return false; |
1733 | |
1734 | if (!src.arrayDimensions().isEmpty()) { |
1735 | if (!encodeArray<qint32>(src: src.arrayDimensions())) |
1736 | return false; |
1737 | } |
1738 | |
1739 | return true; |
1740 | } |
1741 | |
1742 | QT_END_NAMESPACE |
1743 |
Definitions
- QOpcUaBinaryDataEncoding
- QOpcUaBinaryDataEncoding
- enoughData
- offset
- setOffset
- truncateBufferToOffset
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- decode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
- decode
- encode
- encode
- encode
- encode
- encode
- encode
- encode
Start learning QML with our Intro Training
Find out more