| 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 | using 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 | */ |
| 200 | QOpcUaBinaryDataEncoding::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 | */ |
| 210 | QOpcUaBinaryDataEncoding::QOpcUaBinaryDataEncoding(QOpcUaExtensionObject &object) |
| 211 | : m_data(&object.encodedBodyRef()) |
| 212 | { |
| 213 | } |
| 214 | |
| 215 | bool 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 | */ |
| 225 | int 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 | */ |
| 234 | void 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 | */ |
| 246 | void 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 | |
| 255 | template<> |
| 256 | bool 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 | |
| 274 | template<> |
| 275 | QString 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 | |
| 306 | template <> |
| 307 | QOpcUaQualifiedName 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 | |
| 321 | template <> |
| 322 | QOpcUaLocalizedText 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 | |
| 342 | template <> |
| 343 | QOpcUaEUInformation 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 | |
| 366 | template <> |
| 367 | QOpcUaRange 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 | |
| 382 | template <> |
| 383 | QOpcUaComplexNumber 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 | |
| 398 | template <> |
| 399 | QOpcUaDoubleComplexNumber 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 | |
| 414 | template <> |
| 415 | QOpcUaAxisInformation 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 | |
| 442 | template <> |
| 443 | QOpcUaXValue 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 | |
| 458 | template <> |
| 459 | QUuid 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 | |
| 498 | template <> |
| 499 | QByteArray 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 | |
| 524 | template <> |
| 525 | QString 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 | |
| 593 | template <> |
| 594 | QOpcUaExpandedNodeId 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 | |
| 630 | template <> |
| 631 | QDateTime 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 | |
| 645 | template <> |
| 646 | QOpcUa::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 | |
| 655 | template <> |
| 656 | QOpcUaExtensionObject 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 | |
| 682 | template <> |
| 683 | QOpcUaArgument 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 | |
| 710 | template <> |
| 711 | QOpcUaStructureField 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 | |
| 746 | template <> |
| 747 | QOpcUaStructureDefinition 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 | |
| 770 | template <> |
| 771 | QOpcUaEnumField 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 | |
| 794 | template <> |
| 795 | QOpcUaEnumDefinition 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 | |
| 806 | template <> |
| 807 | QOpcUaDiagnosticInfo 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 | |
| 867 | template <> |
| 868 | QOpcUaVariant QOpcUaBinaryDataEncoding::decode<QOpcUaVariant>(bool &success); |
| 869 | |
| 870 | template <> |
| 871 | QOpcUaDataValue 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 | |
| 917 | template <> |
| 918 | QOpcUaVariant 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 | |
| 1035 | template<> |
| 1036 | bool 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 | |
| 1046 | template<> |
| 1047 | bool 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 | |
| 1062 | template<> |
| 1063 | bool 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 | |
| 1072 | template<> |
| 1073 | bool 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 | |
| 1091 | template <> |
| 1092 | bool 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 | |
| 1101 | template <> |
| 1102 | bool 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 | |
| 1115 | template <> |
| 1116 | bool 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 | |
| 1125 | template <> |
| 1126 | bool 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 | |
| 1135 | template <> |
| 1136 | bool 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 | |
| 1151 | template <> |
| 1152 | bool 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 | |
| 1161 | template <> |
| 1162 | bool 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 | |
| 1177 | template <> |
| 1178 | bool 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 | |
| 1193 | template <> |
| 1194 | bool 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 | |
| 1303 | template <> |
| 1304 | bool 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 | |
| 1334 | template <> |
| 1335 | bool 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 | |
| 1358 | template <> |
| 1359 | bool QOpcUaBinaryDataEncoding::encode<QOpcUa::UaStatusCode>(const QOpcUa::UaStatusCode &src) |
| 1360 | { |
| 1361 | if (!encode<quint32>(src)) |
| 1362 | return false; |
| 1363 | return true; |
| 1364 | } |
| 1365 | |
| 1366 | template <> |
| 1367 | bool 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 | |
| 1381 | template <> |
| 1382 | bool 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 | |
| 1405 | template <> |
| 1406 | QOpcUaApplicationRecordDataType 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 | |
| 1441 | template <> |
| 1442 | bool 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 | |
| 1461 | template <> |
| 1462 | bool 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 | |
| 1481 | template <> |
| 1482 | bool 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 | |
| 1495 | template <> |
| 1496 | bool 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 | |
| 1509 | template <> |
| 1510 | bool QOpcUaBinaryDataEncoding::encode<QOpcUaEnumDefinition>(const QOpcUaEnumDefinition &src) |
| 1511 | { |
| 1512 | if (!encodeArray<QOpcUaEnumField>(src: src.fields())) |
| 1513 | return false; |
| 1514 | return true; |
| 1515 | } |
| 1516 | |
| 1517 | template <> |
| 1518 | bool 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 | |
| 1577 | template <> |
| 1578 | bool 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 | |
| 1636 | template <> |
| 1637 | bool 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 | |
| 1744 | QT_END_NAMESPACE |
| 1745 | |