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

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