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