| 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 | #ifndef QOPCUABINARYDATAENCODING_H | 
| 5 | #define QOPCUABINARYDATAENCODING_H | 
| 6 |  | 
| 7 | #include <QtOpcUa/qopcuaglobal.h> | 
| 8 | #include <QtOpcUa/qopcuatype.h> | 
| 9 | #include <QtOpcUa/qopcuavariant.h> | 
| 10 |  | 
| 11 | #include <QtCore/qendian.h> | 
| 12 | #include <QtCore/qlist.h> | 
| 13 |  | 
| 14 | #include <limits> | 
| 15 |  | 
| 16 | QT_BEGIN_NAMESPACE | 
| 17 |  | 
| 18 | class QOpcUaApplicationRecordDataType; | 
| 19 | class QOpcUaArgument; | 
| 20 | class QOpcUaAxisInformation; | 
| 21 | class QOpcUaComplexNumber; | 
| 22 | class QOpcUaDataValue; | 
| 23 | class QOpcUaDiagnosticInfo; | 
| 24 | class QOpcUaDoubleComplexNumber; | 
| 25 | class QOpcUaEnumDefinition; | 
| 26 | class QOpcUaEnumField; | 
| 27 | class QOpcUaEUInformation; | 
| 28 | class QOpcUaExpandedNodeId; | 
| 29 | class QOpcUaExtensionObject; | 
| 30 | class QOpcUaLocalizedText; | 
| 31 | class QOpcUaQualifiedName; | 
| 32 | class QOpcUaRange; | 
| 33 | class QOpcUaStructureDefinition; | 
| 34 | class QOpcUaStructureField; | 
| 35 | class QOpcUaXValue; | 
| 36 |  | 
| 37 | class QByteArray; | 
| 38 | class QDateTime; | 
| 39 | class QUuid; | 
| 40 |  | 
| 41 |  | 
| 42 | // This class implements a subset of the OPC UA Binary DataEncoding defined in OPC UA 1.05 part 6, 5.2. | 
| 43 | class Q_OPCUA_EXPORT QOpcUaBinaryDataEncoding | 
| 44 | { | 
| 45 | public: | 
| 46 |  | 
| 47 |     QOpcUaBinaryDataEncoding(QByteArray *buffer); | 
| 48 |     QOpcUaBinaryDataEncoding(QOpcUaExtensionObject &object); | 
| 49 |  | 
| 50 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 51 |     T decode(bool &success); | 
| 52 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 53 |     QList<T> decodeArray(bool &success); | 
| 54 |  | 
| 55 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 56 |     bool encode(const T &src); | 
| 57 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 58 |     bool encodeArray(const QList<T> &src); | 
| 59 |  | 
| 60 |  | 
| 61 |     int offset() const; | 
| 62 |     void setOffset(int offset); | 
| 63 |     void truncateBufferToOffset(); | 
| 64 |  | 
| 65 | private: | 
| 66 |     bool enoughData(int requiredSize); | 
| 67 |     template <typename T> | 
| 68 |     T upperBound(); | 
| 69 |  | 
| 70 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 71 |     bool encodeValueArrayOrScalar(const QOpcUaVariant &var) { | 
| 72 |         return var.isArray() ? encodeArray<T, OVERLAY>(var.value().value<QList<T>>()) | 
| 73 |                              : encode<T, OVERLAY>(var.value().value<T>()); | 
| 74 |     } | 
| 75 |  | 
| 76 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 77 |     QVariant decodeValueArrayOrScalar(bool isArray, bool &success) { | 
| 78 |         return isArray ? QVariant::fromValue(decodeArray<T, OVERLAY>(success)) : | 
| 79 |                    QVariant::fromValue(decode<T, OVERLAY>(success)); | 
| 80 |     } | 
| 81 |  | 
| 82 |     QByteArray *m_data{nullptr}; | 
| 83 |     int m_offset{0}; | 
| 84 | }; | 
| 85 |  | 
| 86 | template<typename T, QOpcUa::Types OVERLAY> | 
| 87 | T QOpcUaBinaryDataEncoding::decode(bool &success) | 
| 88 | { | 
| 89 |     static_assert(OVERLAY == QOpcUa::Types::Undefined, "Ambiguous types are only permitted for template specializations" ); | 
| 90 |     static_assert(std::is_arithmetic<T>::value == true, "Non-numeric types are only permitted for template specializations" ); | 
| 91 |  | 
| 92 |     if (!m_data) { | 
| 93 |         success = false; | 
| 94 |         return T(0); | 
| 95 |     } | 
| 96 |  | 
| 97 |     if (enoughData(requiredSize: sizeof(T))) { | 
| 98 |         T temp; | 
| 99 |         memcpy(&temp, m_data->constData() + m_offset, sizeof(T)); | 
| 100 |         m_offset += sizeof(T); | 
| 101 |         success = true; | 
| 102 |         return qFromLittleEndian<T>(temp); | 
| 103 |     } else { | 
| 104 |         success = false; | 
| 105 |         return T(0); | 
| 106 |     } | 
| 107 | } | 
| 108 |  | 
| 109 | template<typename T, QOpcUa::Types OVERLAY> | 
| 110 | bool QOpcUaBinaryDataEncoding::encode(const T &src) | 
| 111 | { | 
| 112 |     static_assert(OVERLAY == QOpcUa::Types::Undefined, "Ambiguous types are only permitted for template specializations" ); | 
| 113 |     static_assert(std::is_arithmetic<T>::value == true, "Non-numeric types are only permitted for template specializations" ); | 
| 114 |  | 
| 115 |     if (!m_data) | 
| 116 |         return false; | 
| 117 |  | 
| 118 |     T temp = qToLittleEndian<T>(src); | 
| 119 |     m_data->append(s: reinterpret_cast<const char *>(&temp), len: sizeof(T)); | 
| 120 |     return true; | 
| 121 | } | 
| 122 |  | 
| 123 | template<typename T, QOpcUa::Types OVERLAY> | 
| 124 | QList<T> QOpcUaBinaryDataEncoding::decodeArray(bool &success) | 
| 125 | { | 
| 126 |     QList<T> temp; | 
| 127 |  | 
| 128 |     qint32 size = decode<qint32>(success); | 
| 129 |     if (!success) | 
| 130 |         return temp; | 
| 131 |  | 
| 132 |     for (int i = 0; i < size; ++i) { | 
| 133 |         temp.push_back(decode<T, OVERLAY>(success)); | 
| 134 |         if (!success) | 
| 135 |             return QList<T>(); | 
| 136 |     } | 
| 137 |  | 
| 138 |     return temp; | 
| 139 | } | 
| 140 |  | 
| 141 | template<typename T, QOpcUa::Types OVERLAY> | 
| 142 | bool QOpcUaBinaryDataEncoding::encodeArray(const QList<T> &src) | 
| 143 | { | 
| 144 |     if (src.size() > upperBound<qint32>()) | 
| 145 |         return false; | 
| 146 |  | 
| 147 |     if (!encode<qint32>(src: int(src.size()))) | 
| 148 |         return false; | 
| 149 |     for (const auto &element : src) { | 
| 150 |         if (!encode<T, OVERLAY>(element)) | 
| 151 |             return false; | 
| 152 |     } | 
| 153 |     return true; | 
| 154 | } | 
| 155 |  | 
| 156 | template<typename T> | 
| 157 | T QOpcUaBinaryDataEncoding::upperBound() | 
| 158 | { | 
| 159 |     // Use extra parentheses to prevent macro substitution for max() on windows | 
| 160 |     return (std::numeric_limits<T>::max)(); | 
| 161 | } | 
| 162 |  | 
| 163 | template<> | 
| 164 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<bool>(const bool &src); | 
| 165 |  | 
| 166 | template<> | 
| 167 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QString>(const QString &src); | 
| 168 |  | 
| 169 | template<> | 
| 170 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaQualifiedName>(const QOpcUaQualifiedName &src); | 
| 171 |  | 
| 172 | template<> | 
| 173 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaLocalizedText>(const QOpcUaLocalizedText &src); | 
| 174 |  | 
| 175 | template <> | 
| 176 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaRange>(const QOpcUaRange &src); | 
| 177 |  | 
| 178 | template <> | 
| 179 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaEUInformation>(const QOpcUaEUInformation &src); | 
| 180 |  | 
| 181 | template <> | 
| 182 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaComplexNumber>(const QOpcUaComplexNumber &src); | 
| 183 |  | 
| 184 | template <> | 
| 185 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaDoubleComplexNumber>(const QOpcUaDoubleComplexNumber &src); | 
| 186 |  | 
| 187 | template <> | 
| 188 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaAxisInformation>(const QOpcUaAxisInformation &src); | 
| 189 |  | 
| 190 | template <> | 
| 191 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaXValue>(const QOpcUaXValue &src); | 
| 192 |  | 
| 193 | template <> | 
| 194 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QUuid>(const QUuid &src); | 
| 195 |  | 
| 196 | template <> | 
| 197 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QByteArray>(const QByteArray &src); | 
| 198 |  | 
| 199 | template <> | 
| 200 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QString, QOpcUa::Types::NodeId>(const QString &src); | 
| 201 |  | 
| 202 | template <> | 
| 203 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaExpandedNodeId>(const QOpcUaExpandedNodeId &src); | 
| 204 |  | 
| 205 | template <> | 
| 206 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QDateTime>(const QDateTime &src); | 
| 207 |  | 
| 208 | template <> | 
| 209 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUa::UaStatusCode>(const QOpcUa::UaStatusCode &src); | 
| 210 |  | 
| 211 | template <> | 
| 212 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaExtensionObject>(const QOpcUaExtensionObject &src); | 
| 213 |  | 
| 214 | template <> | 
| 215 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaArgument>(const QOpcUaArgument &src); | 
| 216 |  | 
| 217 | template <> | 
| 218 | Q_OPCUA_EXPORT QOpcUaApplicationRecordDataType QOpcUaBinaryDataEncoding::decode<QOpcUaApplicationRecordDataType>(bool &success); | 
| 219 |  | 
| 220 | template <> | 
| 221 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaApplicationRecordDataType>(const QOpcUaApplicationRecordDataType &src); | 
| 222 |  | 
| 223 | template <> | 
| 224 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaStructureField>(const QOpcUaStructureField &src); | 
| 225 |  | 
| 226 | template <> | 
| 227 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaStructureDefinition>(const QOpcUaStructureDefinition &src); | 
| 228 |  | 
| 229 | template <> | 
| 230 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaEnumField>(const QOpcUaEnumField &src); | 
| 231 |  | 
| 232 | template <> | 
| 233 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaEnumDefinition>(const QOpcUaEnumDefinition &src); | 
| 234 |  | 
| 235 | template <> | 
| 236 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaDiagnosticInfo>(const QOpcUaDiagnosticInfo &src); | 
| 237 |  | 
| 238 | template <> | 
| 239 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaDataValue>(const QOpcUaDataValue &src); | 
| 240 |  | 
| 241 | template <> | 
| 242 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::encode<QOpcUaVariant>(const QOpcUaVariant &src); | 
| 243 |  | 
| 244 | template <> | 
| 245 | Q_OPCUA_EXPORT bool QOpcUaBinaryDataEncoding::decode<bool>(bool &success); | 
| 246 |  | 
| 247 | template <> | 
| 248 | Q_OPCUA_EXPORT QString QOpcUaBinaryDataEncoding::decode<QString>(bool &success); | 
| 249 |  | 
| 250 | template <> | 
| 251 | Q_OPCUA_EXPORT QOpcUaQualifiedName QOpcUaBinaryDataEncoding::decode<QOpcUaQualifiedName>(bool &success); | 
| 252 |  | 
| 253 | template <> | 
| 254 | Q_OPCUA_EXPORT QOpcUaLocalizedText QOpcUaBinaryDataEncoding::decode<QOpcUaLocalizedText>(bool &success); | 
| 255 |  | 
| 256 | template <> | 
| 257 | Q_OPCUA_EXPORT QOpcUaEUInformation QOpcUaBinaryDataEncoding::decode<QOpcUaEUInformation>(bool &success); | 
| 258 |  | 
| 259 | template <> | 
| 260 | Q_OPCUA_EXPORT QOpcUaRange QOpcUaBinaryDataEncoding::decode<QOpcUaRange>(bool &success); | 
| 261 |  | 
| 262 | template <> | 
| 263 | Q_OPCUA_EXPORT QOpcUaComplexNumber QOpcUaBinaryDataEncoding::decode<QOpcUaComplexNumber>(bool &success); | 
| 264 |  | 
| 265 | template <> | 
| 266 | Q_OPCUA_EXPORT QOpcUaDoubleComplexNumber QOpcUaBinaryDataEncoding::decode<QOpcUaDoubleComplexNumber>(bool &success); | 
| 267 |  | 
| 268 | template <> | 
| 269 | Q_OPCUA_EXPORT QOpcUaAxisInformation QOpcUaBinaryDataEncoding::decode<QOpcUaAxisInformation>(bool &success); | 
| 270 |  | 
| 271 | template <> | 
| 272 | Q_OPCUA_EXPORT QOpcUaXValue QOpcUaBinaryDataEncoding::decode<QOpcUaXValue>(bool &success); | 
| 273 |  | 
| 274 | template <> | 
| 275 | Q_OPCUA_EXPORT QUuid QOpcUaBinaryDataEncoding::decode<QUuid>(bool &success); | 
| 276 |  | 
| 277 | template <> | 
| 278 | Q_OPCUA_EXPORT QByteArray QOpcUaBinaryDataEncoding::decode<QByteArray>(bool &success); | 
| 279 |  | 
| 280 | template <> | 
| 281 | Q_OPCUA_EXPORT QString QOpcUaBinaryDataEncoding::decode<QString, QOpcUa::Types::NodeId>(bool &success); | 
| 282 |  | 
| 283 | template <> | 
| 284 | Q_OPCUA_EXPORT QOpcUaExpandedNodeId QOpcUaBinaryDataEncoding::decode<QOpcUaExpandedNodeId>(bool &success); | 
| 285 |  | 
| 286 | template <> | 
| 287 | Q_OPCUA_EXPORT QDateTime QOpcUaBinaryDataEncoding::decode<QDateTime>(bool &success); | 
| 288 |  | 
| 289 | template <> | 
| 290 | Q_OPCUA_EXPORT QOpcUa::UaStatusCode QOpcUaBinaryDataEncoding::decode<QOpcUa::UaStatusCode>(bool &success); | 
| 291 |  | 
| 292 | template <> | 
| 293 | Q_OPCUA_EXPORT QOpcUaExtensionObject QOpcUaBinaryDataEncoding::decode<QOpcUaExtensionObject>(bool &success); | 
| 294 |  | 
| 295 | template <> | 
| 296 | Q_OPCUA_EXPORT QOpcUaArgument QOpcUaBinaryDataEncoding::decode<QOpcUaArgument>(bool &success); | 
| 297 |  | 
| 298 | template <> | 
| 299 | Q_OPCUA_EXPORT QOpcUaStructureField QOpcUaBinaryDataEncoding::decode<QOpcUaStructureField>(bool &success); | 
| 300 |  | 
| 301 | template <> | 
| 302 | Q_OPCUA_EXPORT QOpcUaStructureDefinition QOpcUaBinaryDataEncoding::decode<QOpcUaStructureDefinition>(bool &success); | 
| 303 |  | 
| 304 | template <> | 
| 305 | Q_OPCUA_EXPORT QOpcUaEnumField QOpcUaBinaryDataEncoding::decode<QOpcUaEnumField>(bool &success); | 
| 306 |  | 
| 307 | template <> | 
| 308 | Q_OPCUA_EXPORT QOpcUaEnumDefinition QOpcUaBinaryDataEncoding::decode<QOpcUaEnumDefinition>(bool &success); | 
| 309 |  | 
| 310 | template <> | 
| 311 | Q_OPCUA_EXPORT QOpcUaDiagnosticInfo QOpcUaBinaryDataEncoding::decode<QOpcUaDiagnosticInfo>(bool &success); | 
| 312 |  | 
| 313 | template <> | 
| 314 | Q_OPCUA_EXPORT QOpcUaVariant QOpcUaBinaryDataEncoding::decode<QOpcUaVariant>(bool &success); | 
| 315 |  | 
| 316 | template <> | 
| 317 | Q_OPCUA_EXPORT QOpcUaDataValue QOpcUaBinaryDataEncoding::decode<QOpcUaDataValue>(bool &success); | 
| 318 |  | 
| 319 | QT_END_NAMESPACE | 
| 320 |  | 
| 321 | #endif // QOPCUABINARYDATAENCODING_H | 
| 322 |  |