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
30QT_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*/
198QOpcUaBinaryDataEncoding::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*/
208QOpcUaBinaryDataEncoding::QOpcUaBinaryDataEncoding(QOpcUaExtensionObject &object)
209 : m_data(&object.encodedBodyRef())
210{
211}
212
213bool 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*/
223int 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*/
232void 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*/
244void 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
253template<>
254bool 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
272template<>
273QString 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
304template <>
305QOpcUaQualifiedName 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
319template <>
320QOpcUaLocalizedText 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
340template <>
341QOpcUaEUInformation 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
364template <>
365QOpcUaRange 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
380template <>
381QOpcUaComplexNumber 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
396template <>
397QOpcUaDoubleComplexNumber 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
412template <>
413QOpcUaAxisInformation 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
440template <>
441QOpcUaXValue 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
456template <>
457QUuid 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
496template <>
497QByteArray 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
522template <>
523QString 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
591template <>
592QOpcUaExpandedNodeId 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
628template <>
629QDateTime 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
643template <>
644QOpcUa::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
653template <>
654QOpcUaExtensionObject 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
680template <>
681QOpcUaArgument 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
708template <>
709QOpcUaStructureField 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
744template <>
745QOpcUaStructureDefinition 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
768template <>
769QOpcUaEnumField 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
792template <>
793QOpcUaEnumDefinition 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
804template <>
805QOpcUaDiagnosticInfo 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
865template <>
866QOpcUaVariant QOpcUaBinaryDataEncoding::decode<QOpcUaVariant>(bool &success);
867
868template <>
869QOpcUaDataValue 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
915template <>
916QOpcUaVariant 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
1033template<>
1034bool 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
1044template<>
1045bool 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
1060template<>
1061bool 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
1070template<>
1071bool 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
1089template <>
1090bool 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
1099template <>
1100bool 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
1113template <>
1114bool 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
1123template <>
1124bool 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
1133template <>
1134bool 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
1149template <>
1150bool 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
1159template <>
1160bool 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
1175template <>
1176bool 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
1191template <>
1192bool 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
1301template <>
1302bool 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
1332template <>
1333bool 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
1356template <>
1357bool QOpcUaBinaryDataEncoding::encode<QOpcUa::UaStatusCode>(const QOpcUa::UaStatusCode &src)
1358{
1359 if (!encode<quint32>(src))
1360 return false;
1361 return true;
1362}
1363
1364template <>
1365bool 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
1379template <>
1380bool 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
1403template <>
1404QOpcUaApplicationRecordDataType 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
1439template <>
1440bool 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
1459template <>
1460bool 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
1479template <>
1480bool 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
1493template <>
1494bool 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
1507template <>
1508bool QOpcUaBinaryDataEncoding::encode<QOpcUaEnumDefinition>(const QOpcUaEnumDefinition &src)
1509{
1510 if (!encodeArray<QOpcUaEnumField>(src: src.fields()))
1511 return false;
1512 return true;
1513}
1514
1515template <>
1516bool 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
1575template <>
1576bool 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
1634template <>
1635bool 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
1742QT_END_NAMESPACE
1743

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtopcua/src/opcua/client/qopcuabinarydataencoding.cpp