| 1 | // Copyright (C) 2023 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 "private/qopcuagenericstructhandler_p.h" | 
| 5 |  | 
| 6 | #include <QtOpcUa/qopcuaargument.h> | 
| 7 | #include <QtOpcUa/qopcuaaxisinformation.h> | 
| 8 | #include <QtOpcUa/qopcuabinarydataencoding.h> | 
| 9 | #include <QtOpcUa/qopcuacomplexnumber.h> | 
| 10 | #include <QtOpcUa/qopcuadatavalue.h> | 
| 11 | #include <QtOpcUa/qopcuadiagnosticinfo.h> | 
| 12 | #include <QtOpcUa/qopcuadoublecomplexnumber.h> | 
| 13 | #include <QtOpcUa/qopcuaenumfield.h> | 
| 14 | #include <QtOpcUa/qopcuaeuinformation.h> | 
| 15 | #include <QtOpcUa/qopcuagenericstructhandler.h> | 
| 16 | #include <QtOpcUa/qopcuagenericstructvalue.h> | 
| 17 | #include <QtOpcUa/qopcuarange.h> | 
| 18 | #include <QtOpcUa/qopcuastructurefield.h> | 
| 19 | #include <QtOpcUa/qopcuaxvalue.h> | 
| 20 |  | 
| 21 | #include "qopcuainternaldatatypenode_p.h" | 
| 22 |  | 
| 23 | #include <QtCore/quuid.h> | 
| 24 |  | 
| 25 | QT_BEGIN_NAMESPACE | 
| 26 |  | 
| 27 | Q_LOGGING_CATEGORY(lcGenericStructHandler, "qt.opcuagenericstructhandler" ) | 
| 28 |  | 
| 29 | QOpcUaGenericStructHandlerPrivate::QOpcUaGenericStructHandlerPrivate(QOpcUaClient *client) | 
| 30 |     : m_client(client) | 
| 31 | { | 
| 32 | } | 
| 33 |  | 
| 34 | bool QOpcUaGenericStructHandlerPrivate::initialize() | 
| 35 | { | 
| 36 |     if (!m_client) | 
| 37 |         return false; | 
| 38 |  | 
| 39 |     m_initialized = false; | 
| 40 |  | 
| 41 |     m_baseDataType.reset(other: new QOpcUaInternalDataTypeNode(m_client)); | 
| 42 |  | 
| 43 |     QObjectPrivate::connect(sender: m_baseDataType.get(), signal: &QOpcUaInternalDataTypeNode::initializeFinished, | 
| 44 |                      receiverPrivate: this, slot: &QOpcUaGenericStructHandlerPrivate::handleInitializeFinished); | 
| 45 |  | 
| 46 |     return m_baseDataType->initialize(nodeId: QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::BaseDataType)); | 
| 47 | } | 
| 48 |  | 
| 49 | QOpcUaGenericStructValue QOpcUaGenericStructHandlerPrivate::decode(const QOpcUaExtensionObject &extensionObject, bool &success) const | 
| 50 | { | 
| 51 |     const auto entry = m_structuresByEncodingId.constFind(key: extensionObject.encodingTypeId()); | 
| 52 |     if (entry == m_structuresByEncodingId.constEnd()) { | 
| 53 |         qCWarning(lcGenericStructHandler) << "Failed to find description for"  << extensionObject.encodingTypeId(); | 
| 54 |         success = false; | 
| 55 |         return QOpcUaGenericStructValue(); | 
| 56 |     } | 
| 57 |  | 
| 58 |     qCDebug(lcGenericStructHandler) << "Decoding"  << entry->name << entry.key(); | 
| 59 |  | 
| 60 |     auto data = extensionObject.encodedBody(); | 
| 61 |     QOpcUaBinaryDataEncoding decoder(&data); | 
| 62 |  | 
| 63 |     return decodeStructInternal(decoder, dataTypeId: entry->nodeId, success, currentDepth: 0); | 
| 64 | } | 
| 65 |  | 
| 66 | bool QOpcUaGenericStructHandlerPrivate::encode(const QOpcUaGenericStructValue &value, QOpcUaExtensionObject &output) | 
| 67 | { | 
| 68 |     if (value.structureDefinition().fields().empty()) { | 
| 69 |         qCWarning(lcGenericStructHandler) << "The structure doesn't have any fields, unable to encode" ; | 
| 70 |         return false; | 
| 71 |     } | 
| 72 |  | 
| 73 |     if (value.structureDefinition().defaultEncodingId().isEmpty()) { | 
| 74 |         qCWarning(lcGenericStructHandler) << "The struct doesn't have an encoding ID, unable to encode" ; | 
| 75 |         return false; | 
| 76 |     } | 
| 77 |  | 
| 78 |     output.setEncodingTypeId(value.structureDefinition().defaultEncodingId()); | 
| 79 |     output.setEncoding(QOpcUaExtensionObject::Encoding::ByteString); | 
| 80 |  | 
| 81 |     QOpcUaBinaryDataEncoding encoder(output); | 
| 82 |     return encodeStructInternal(encoder, value); | 
| 83 | } | 
| 84 |  | 
| 85 | QOpcUaGenericStructValue QOpcUaGenericStructHandlerPrivate::createGenericStructValueForTypeId(const QString &typeId) | 
| 86 | { | 
| 87 |     const auto entry = m_structuresByTypeId.constFind(key: typeId); | 
| 88 |  | 
| 89 |     if (entry != m_structuresByTypeId.constEnd()) { | 
| 90 |         QOpcUaGenericStructValue value(entry.value().name, typeId, entry.value().structureDefinition); | 
| 91 |  | 
| 92 |         if (entry.value().structureDefinition.structureType() != QOpcUaStructureDefinition::StructureType::Union) { | 
| 93 |             for (const auto &field : entry.value().structureDefinition.fields()) { | 
| 94 |                 if (field.isOptional()) | 
| 95 |                     continue; | 
| 96 |  | 
| 97 |                 value.fieldsRef().insert(key: field.name(), value: QVariant()); | 
| 98 |             } | 
| 99 |         } | 
| 100 |  | 
| 101 |         return value; | 
| 102 |     } | 
| 103 |  | 
| 104 |     qCWarning(lcGenericStructHandler) << "Unable to create a pre-filled generic struct value for"  << typeId; | 
| 105 |     return {}; | 
| 106 | } | 
| 107 |  | 
| 108 | QOpcUaStructureDefinition QOpcUaGenericStructHandlerPrivate::structureDefinitionForBinaryEncodingId(const QString &id) const | 
| 109 | { | 
| 110 |     const auto entry = m_structuresByEncodingId.constFind(key: id); | 
| 111 |     return entry != m_structuresByEncodingId.constEnd() ? entry.value().structureDefinition : QOpcUaStructureDefinition(); | 
| 112 | } | 
| 113 |  | 
| 114 | QOpcUaStructureDefinition QOpcUaGenericStructHandlerPrivate::structureDefinitionForTypeId(const QString &id) const | 
| 115 | { | 
| 116 |     const auto entry = m_structuresByTypeId.constFind(key: id); | 
| 117 |     return entry != m_structuresByTypeId.constEnd() ? entry.value().structureDefinition : QOpcUaStructureDefinition(); | 
| 118 | } | 
| 119 |  | 
| 120 | QOpcUaEnumDefinition QOpcUaGenericStructHandlerPrivate::enumDefinitionForTypeId(const QString &id) const | 
| 121 | { | 
| 122 |     const auto entry = m_enumsByTypeId.constFind(key: id); | 
| 123 |     return entry != m_enumsByTypeId.constEnd() ? entry.value().enumDefinition : QOpcUaEnumDefinition(); | 
| 124 | } | 
| 125 |  | 
| 126 | QString QOpcUaGenericStructHandlerPrivate::typeNameForBinaryEncodingId(const QString &id) const | 
| 127 | { | 
| 128 |     const auto entry = m_typeNamesByEncodingId.constFind(key: id); | 
| 129 |     return entry != m_typeNamesByEncodingId.constEnd() ? entry.value() : QString(); | 
| 130 | } | 
| 131 |  | 
| 132 | QString QOpcUaGenericStructHandlerPrivate::typeNameForTypeId(const QString &id) const | 
| 133 | { | 
| 134 |     const auto entry = m_typeNamesByTypeId.constFind(key: id); | 
| 135 |     return entry != m_typeNamesByTypeId.constEnd() ? entry.value() : QString(); | 
| 136 | } | 
| 137 |  | 
| 138 | QOpcUaGenericStructHandler::DataTypeKind QOpcUaGenericStructHandlerPrivate::dataTypeKindForTypeId(const QString &id) const | 
| 139 | { | 
| 140 |     const auto isStruct = m_structuresByTypeId.constFind(key: id); | 
| 141 |     if (isStruct != m_structuresByTypeId.constEnd()) | 
| 142 |         return QOpcUaGenericStructHandler::DataTypeKind::Struct; | 
| 143 |  | 
| 144 |     const auto isEnum = m_enumsByTypeId.constFind(key: id); | 
| 145 |     if (isEnum != m_enumsByTypeId.constEnd()) | 
| 146 |         return QOpcUaGenericStructHandler::DataTypeKind::Enum; | 
| 147 |  | 
| 148 |     return typeNameForTypeId(id).isEmpty() ? | 
| 149 |                QOpcUaGenericStructHandler::DataTypeKind::Unknown : QOpcUaGenericStructHandler::DataTypeKind::Other; | 
| 150 | } | 
| 151 |  | 
| 152 | QString QOpcUaGenericStructHandlerPrivate::typeIdForBinaryEncodingId(const QString &id) const | 
| 153 | { | 
| 154 |     const auto entry = m_structuresByEncodingId.constFind(key: id); | 
| 155 |     return entry != m_structuresByEncodingId.constEnd() ? entry->nodeId : QString(); | 
| 156 | } | 
| 157 |  | 
| 158 | bool QOpcUaGenericStructHandlerPrivate::isAbstractTypeId(const QString &id) const | 
| 159 | { | 
| 160 |     return m_abstractTypeIds.contains(value: id); | 
| 161 | } | 
| 162 |  | 
| 163 | QOpcUaGenericStructValue QOpcUaGenericStructHandlerPrivate::decodeStructInternal(QOpcUaBinaryDataEncoding &decoder, | 
| 164 |                                                                                  const QString &dataTypeId, bool &success, | 
| 165 |                                                                                  int currentDepth) const | 
| 166 | { | 
| 167 |     if (currentDepth > m_maxNestingLevel) { | 
| 168 |         qCWarning(lcGenericStructHandler) << "Maximum nesting level of"  << m_maxNestingLevel << "exceeded" ; | 
| 169 |         success = false; | 
| 170 |         return QOpcUaGenericStructValue(); | 
| 171 |     } | 
| 172 |  | 
| 173 |     const auto entry = m_structuresByTypeId.constFind(key: dataTypeId); | 
| 174 |     if (entry == m_structuresByTypeId.constEnd()) { | 
| 175 |         qCWarning(lcGenericStructHandler) << "Failed to find description for"  << dataTypeId; | 
| 176 |         success = false; | 
| 177 |         return QOpcUaGenericStructValue(); | 
| 178 |     } | 
| 179 |  | 
| 180 |     if (entry->isAbstract) { | 
| 181 |         qCWarning(lcGenericStructHandler) << "Decoding of abstract struct"  << entry->name << "requested" ; | 
| 182 |         success = false; | 
| 183 |         return QOpcUaGenericStructValue(); | 
| 184 |     } | 
| 185 |  | 
| 186 |     if (entry->structureDefinition.fields().isEmpty()) { | 
| 187 |         qCWarning(lcGenericStructHandler) << "Missing fields information for struct"  << entry->name; | 
| 188 |         success = false; | 
| 189 |         return QOpcUaGenericStructValue(); | 
| 190 |     } | 
| 191 |  | 
| 192 |     QOpcUaGenericStructValue result(entry->name, entry->nodeId, entry->structureDefinition); | 
| 193 |     auto &fields = result.fieldsRef(); | 
| 194 |  | 
| 195 |     if (entry->structureDefinition.structureType() == QOpcUaStructureDefinition::StructureType::Structure) { | 
| 196 |         for (const auto &field : entry->structureDefinition.fields()) { | 
| 197 |             fields[field.name()] = decodeKnownTypesInternal(decoder, dataTypeId: field.dataType(), valueRank: field.valueRank(), success, currentDepth: currentDepth + 1); | 
| 198 |             if (!success) { | 
| 199 |                 qCWarning(lcGenericStructHandler) << "Failed to decode struct field" ; | 
| 200 |                 return QOpcUaGenericStructValue(); | 
| 201 |             } | 
| 202 |         } | 
| 203 |     } else if (entry->structureDefinition.structureType() == QOpcUaStructureDefinition::StructureType::Union) { | 
| 204 |         auto switchField = decoder.decode<quint32>(success); | 
| 205 |         if (!success) { | 
| 206 |             qCWarning(lcGenericStructHandler) << "Failed to decode the union switch field" ; | 
| 207 |             return QOpcUaGenericStructValue(); | 
| 208 |         } | 
| 209 |  | 
| 210 |         if (!switchField) | 
| 211 |             return result; // Empty union, no need to continue processing | 
| 212 |  | 
| 213 |         if (switchField > static_cast<quint32>(entry->structureDefinition.fields().size())) { | 
| 214 |             qCWarning(lcGenericStructHandler) << "Union switch field out of bounds" ; | 
| 215 |             success = false; | 
| 216 |             return QOpcUaGenericStructValue(); | 
| 217 |         } | 
| 218 |  | 
| 219 |         qCDebug(lcGenericStructHandler) << "Decode union field with switch value"  << switchField; | 
| 220 |  | 
| 221 |         auto field = entry->structureDefinition.fields().at(i: switchField - 1); | 
| 222 |  | 
| 223 |         fields[field.name()] = decodeKnownTypesInternal(decoder, dataTypeId: field.dataType(), valueRank: field.valueRank() > 0, success, currentDepth: currentDepth + 1); | 
| 224 |         if (!success) { | 
| 225 |             qCWarning(lcGenericStructHandler) << "Failed to decode union content" ; | 
| 226 |             return QOpcUaGenericStructValue(); | 
| 227 |         } | 
| 228 |     } else if (entry->structureDefinition.structureType() == QOpcUaStructureDefinition::StructureType::StructureWithOptionalFields) { | 
| 229 |         auto mask = decoder.decode<quint32>(success); | 
| 230 |         if (!success) { | 
| 231 |             qCWarning(lcGenericStructHandler) << "Failed to decode the optional fields mask" ; | 
| 232 |             return QOpcUaGenericStructValue(); | 
| 233 |         } | 
| 234 |  | 
| 235 |         int optionalFieldIndex = 0; | 
| 236 |         for (const auto &field : entry->structureDefinition.fields()) { | 
| 237 |             if (field.isOptional() && !(mask & (1 << optionalFieldIndex++))) | 
| 238 |                 continue; | 
| 239 |  | 
| 240 |             fields[field.name()] = decodeKnownTypesInternal(decoder, dataTypeId: field.dataType(), valueRank: field.valueRank() > 0, success, currentDepth: currentDepth + 1); | 
| 241 |             if (!success) { | 
| 242 |                 qCWarning(lcGenericStructHandler) << "Failed to decode struct field" ; | 
| 243 |                 return QOpcUaGenericStructValue(); | 
| 244 |             } | 
| 245 |         } | 
| 246 |     } | 
| 247 |  | 
| 248 |     return result; | 
| 249 | } | 
| 250 |  | 
| 251 | QVariant QOpcUaGenericStructHandlerPrivate::decodeKnownTypesInternal(QOpcUaBinaryDataEncoding &decoder, const QString &dataTypeId, | 
| 252 |                                                                      qint32 valueRank, bool &success, int currentDepth) const | 
| 253 | { | 
| 254 |     if (currentDepth > m_maxNestingLevel) { | 
| 255 |         qCWarning(lcGenericStructHandler) << "Maximum nesting level of"  << m_maxNestingLevel << "exceeded" ; | 
| 256 |         success = false; | 
| 257 |         return QOpcUaGenericStructValue(); | 
| 258 |     } | 
| 259 |  | 
| 260 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Boolean)) | 
| 261 |         return decodeArrayOrScalar<bool>(decoder, valueRank, success); | 
| 262 |  | 
| 263 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Byte)) | 
| 264 |         return decodeArrayOrScalar<quint8>(decoder, valueRank, success); | 
| 265 |  | 
| 266 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::SByte)) | 
| 267 |         return decodeArrayOrScalar<qint8>(decoder, valueRank, success); | 
| 268 |  | 
| 269 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt16)) | 
| 270 |         return decodeArrayOrScalar<quint16>(decoder, valueRank, success); | 
| 271 |  | 
| 272 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int16)) | 
| 273 |         return decodeArrayOrScalar<qint16>(decoder, valueRank, success); | 
| 274 |  | 
| 275 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt32)) | 
| 276 |         return decodeArrayOrScalar<quint32>(decoder, valueRank, success); | 
| 277 |  | 
| 278 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int32)) | 
| 279 |         return decodeArrayOrScalar<qint32>(decoder, valueRank, success); | 
| 280 |  | 
| 281 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt64)) | 
| 282 |         return decodeArrayOrScalar<quint64>(decoder, valueRank, success); | 
| 283 |  | 
| 284 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int64)) | 
| 285 |         return decodeArrayOrScalar<qint64>(decoder, valueRank, success); | 
| 286 |  | 
| 287 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Float)) | 
| 288 |         return decodeArrayOrScalar<float>(decoder, valueRank, success); | 
| 289 |  | 
| 290 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Double)) | 
| 291 |         return decodeArrayOrScalar<double>(decoder, valueRank, success); | 
| 292 |  | 
| 293 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StatusCode)) | 
| 294 |         return decodeArrayOrScalar<QOpcUa::UaStatusCode>(decoder, valueRank, success); | 
| 295 |  | 
| 296 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DateTime)) | 
| 297 |         return decodeArrayOrScalar<QDateTime>(decoder, valueRank, success); | 
| 298 |  | 
| 299 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::String)) | 
| 300 |         return decodeArrayOrScalar<QString>(decoder, valueRank, success); | 
| 301 |  | 
| 302 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::NodeId)) | 
| 303 |         return decodeArrayOrScalar<QString, QOpcUa::Types::NodeId>(decoder, valueRank, success); | 
| 304 |  | 
| 305 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ByteString)) | 
| 306 |         return decodeArrayOrScalar<QByteArray>(decoder, valueRank, success); | 
| 307 |  | 
| 308 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::XmlElement)) | 
| 309 |         return decodeArrayOrScalar<QByteArray>(decoder, valueRank, success); | 
| 310 |  | 
| 311 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Guid)) | 
| 312 |         return decodeArrayOrScalar<QUuid>(decoder, valueRank, success); | 
| 313 |  | 
| 314 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::QualifiedName)) | 
| 315 |         return decodeArrayOrScalar<QOpcUaQualifiedName>(decoder, valueRank, success); | 
| 316 |  | 
| 317 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::LocalizedText)) | 
| 318 |         return decodeArrayOrScalar<QOpcUaLocalizedText>(decoder, valueRank, success); | 
| 319 |  | 
| 320 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Range)) | 
| 321 |         return decodeArrayOrScalar<QOpcUaRange>(decoder, valueRank, success); | 
| 322 |  | 
| 323 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::EUInformation)) | 
| 324 |         return decodeArrayOrScalar<QOpcUaEUInformation>(decoder, valueRank, success); | 
| 325 |  | 
| 326 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ComplexNumberType)) | 
| 327 |         return decodeArrayOrScalar<QOpcUaComplexNumber>(decoder, valueRank, success); | 
| 328 |  | 
| 329 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DoubleComplexNumberType)) | 
| 330 |         return decodeArrayOrScalar<QOpcUaDoubleComplexNumber>(decoder, valueRank, success); | 
| 331 |  | 
| 332 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::AxisInformation)) | 
| 333 |         return decodeArrayOrScalar<QOpcUaAxisInformation>(decoder, valueRank, success); | 
| 334 |  | 
| 335 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::XVType)) | 
| 336 |         return decodeArrayOrScalar<QOpcUaXValue>(decoder, valueRank, success); | 
| 337 |  | 
| 338 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ExpandedNodeId)) | 
| 339 |         return decodeArrayOrScalar<QOpcUaExpandedNodeId>(decoder, valueRank, success); | 
| 340 |  | 
| 341 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Argument)) | 
| 342 |         return decodeArrayOrScalar<QOpcUaArgument>(decoder, valueRank, success); | 
| 343 |  | 
| 344 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StructureDefinition)) | 
| 345 |         return decodeArrayOrScalar<QOpcUaStructureDefinition>(decoder, valueRank, success); | 
| 346 |  | 
| 347 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StructureField)) | 
| 348 |         return decodeArrayOrScalar<QOpcUaStructureField>(decoder, valueRank, success); | 
| 349 |  | 
| 350 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::EnumDefinition)) | 
| 351 |         return decodeArrayOrScalar<QOpcUaEnumDefinition>(decoder, valueRank, success); | 
| 352 |  | 
| 353 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::EnumField)) | 
| 354 |         return decodeArrayOrScalar<QOpcUaEnumField>(decoder, valueRank, success); | 
| 355 |  | 
| 356 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DiagnosticInfo)) { | 
| 357 |         return decodeArrayOrScalar<QOpcUaDiagnosticInfo>(decoder, valueRank, success); | 
| 358 |     } | 
| 359 |  | 
| 360 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DataValue)) | 
| 361 |         return decodeArrayOrScalar<QOpcUaDataValue>(decoder, valueRank, success); | 
| 362 |  | 
| 363 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::BaseDataType)) | 
| 364 |         return decodeArrayOrScalar<QOpcUaVariant>(decoder, valueRank, success); | 
| 365 |  | 
| 366 |     // Enumeration | 
| 367 |     const auto enumType = m_enumsByTypeId.constFind(key: dataTypeId); | 
| 368 |     if (enumType != m_enumsByTypeId.constEnd()) { | 
| 369 |         if (enumType->isAbstract) { | 
| 370 |             qCWarning(lcGenericStructHandler) << "Decoding abstract enum"  << enumType->name << "requested" ; | 
| 371 |             success = false; | 
| 372 |             return QVariant(); | 
| 373 |         } | 
| 374 |  | 
| 375 |         const auto enumValue = decodeArrayOrScalar<qint32>(decoder, valueRank, success); | 
| 376 |         if (!success) { | 
| 377 |             qCWarning(lcGenericStructHandler) << "Failed to decode enum" ; | 
| 378 |             return QVariant(); | 
| 379 |         } | 
| 380 |         return enumValue; | 
| 381 |     } | 
| 382 |  | 
| 383 |     // Fallback for nested unknown structs | 
| 384 |     if (valueRank > 0) { | 
| 385 |         QList<quint32> arrayDimensions; | 
| 386 |         if (valueRank > 1) { | 
| 387 |             arrayDimensions = decoder.decodeArray<quint32>(success); | 
| 388 |  | 
| 389 |             if (!success) | 
| 390 |                 return QVariant(); | 
| 391 |         } | 
| 392 |  | 
| 393 |         const auto arrayLength = decoder.decode<qint32>(success); | 
| 394 |         if (!success) | 
| 395 |             return QVariant(); | 
| 396 |  | 
| 397 |         QList<QOpcUaGenericStructValue> result; | 
| 398 |         for (int i = 0; i < arrayLength; ++i) { | 
| 399 |             result.append(t: decodeStructInternal(decoder, dataTypeId, success, currentDepth: currentDepth + 1)); | 
| 400 |             if (!success) { | 
| 401 |                 qCWarning(lcGenericStructHandler) << "Failed to decode nested struct array" ; | 
| 402 |                 return QVariant(); | 
| 403 |             } | 
| 404 |         } | 
| 405 |  | 
| 406 |         if (!arrayDimensions.isEmpty()) { | 
| 407 |             QVariantList data(result.constBegin(), result.constEnd()); | 
| 408 |             return QOpcUaMultiDimensionalArray(data, arrayDimensions); | 
| 409 |         } | 
| 410 |  | 
| 411 |         return QVariant::fromValue(value: result); | 
| 412 |     } else { | 
| 413 |         const auto result = decodeStructInternal(decoder, dataTypeId, success, currentDepth: currentDepth + 1); | 
| 414 |         if (!success) { | 
| 415 |             qCWarning(lcGenericStructHandler) << "Failed to decode nested struct" ; | 
| 416 |             return QVariant(); | 
| 417 |         } | 
| 418 |         return result; | 
| 419 |     } | 
| 420 |  | 
| 421 |     const auto superType = m_knownSubtypes.constFind(key: dataTypeId); | 
| 422 |     if (superType != m_knownSubtypes.constEnd()) | 
| 423 |         return decodeKnownTypesInternal(decoder, dataTypeId: superType.value(), valueRank, success, currentDepth: currentDepth + 1); | 
| 424 |  | 
| 425 |     success = false; | 
| 426 |     return QVariant(); | 
| 427 | } | 
| 428 |  | 
| 429 | bool QOpcUaGenericStructHandlerPrivate::encodeStructInternal(QOpcUaBinaryDataEncoding &encoder, const QOpcUaGenericStructValue &value) | 
| 430 | { | 
| 431 |     const auto entry = m_structuresByTypeId.constFind(key: value.typeId()); | 
| 432 |  | 
| 433 |     if (entry == m_structuresByTypeId.constEnd()) { | 
| 434 |         qCWarning(lcGenericStructHandler) << "Failed to find description for"  << value.typeId(); | 
| 435 |         return false; | 
| 436 |     } | 
| 437 |  | 
| 438 |     if (entry->isAbstract) { | 
| 439 |         qCWarning(lcGenericStructHandler) << "Decoding of abstract struct"  << entry->name << "requested" ; | 
| 440 |         return false; | 
| 441 |     } | 
| 442 |  | 
| 443 |     if (entry->structureDefinition.fields().isEmpty()) { | 
| 444 |         qCWarning(lcGenericStructHandler) << "Missing fields information for struct"  << entry->name; | 
| 445 |         return false; | 
| 446 |     } | 
| 447 |  | 
| 448 |     if (value.structureDefinition().structureType() == QOpcUaStructureDefinition::StructureType::Structure) { | 
| 449 |         for (const auto &field : value.structureDefinition().fields()) { | 
| 450 |             if (!value.fields().contains(key: field.name())) { | 
| 451 |                 qCWarning(lcGenericStructHandler) << "Field"  << field.name() << "is missing, unable to encode struct" ; | 
| 452 |                 return false; | 
| 453 |             } | 
| 454 |             const auto success = encodeKnownTypesInternal(encoder, value: value.fields().value(key: field.name()), valueRank: field.valueRank(), dataTypeId: field.dataType()); | 
| 455 |             if (!success) { | 
| 456 |                 qCWarning(lcGenericStructHandler) << "Failed to encode struct field"  << field.name(); | 
| 457 |                 return false; | 
| 458 |             } | 
| 459 |         } | 
| 460 |  | 
| 461 |         return true; | 
| 462 |     } else if (value.structureDefinition().structureType() == QOpcUaStructureDefinition::StructureType::StructureWithOptionalFields) { | 
| 463 |         quint32 mask = 0; | 
| 464 |         quint32 index = 0; | 
| 465 |         for (const auto &field : value.structureDefinition().fields()) { | 
| 466 |             if (!field.isOptional()) | 
| 467 |                 continue; | 
| 468 |  | 
| 469 |             if (value.fields().contains(key: field.name())) | 
| 470 |                 mask |= (1 << index); | 
| 471 |  | 
| 472 |             ++index; | 
| 473 |         } | 
| 474 |  | 
| 475 |         auto success = encoder.encode<quint32>(src: mask); | 
| 476 |  | 
| 477 |         if (!success) { | 
| 478 |             qCWarning(lcGenericStructHandler) << "Failed to encode optional fields mask" ; | 
| 479 |             return false; | 
| 480 |         } | 
| 481 |  | 
| 482 |         for (const auto &field : value.structureDefinition().fields()) { | 
| 483 |             if (!field.isOptional() && !value.fields().contains(key: field.name())) { | 
| 484 |                 qCWarning(lcGenericStructHandler) << "Field"  << field.name() << "is missing, unable to encode struct" ; | 
| 485 |                 return false; | 
| 486 |             } | 
| 487 |  | 
| 488 |             if (value.fields().contains(key: field.name())) { | 
| 489 |                 const auto success = encodeKnownTypesInternal(encoder, value: value.fields().value(key: field.name()), valueRank: field.valueRank(), dataTypeId: field.dataType()); | 
| 490 |                 if (!success) { | 
| 491 |                     qCWarning(lcGenericStructHandler) << "Failed to encode struct field"  << field.name(); | 
| 492 |                     return false; | 
| 493 |                 } | 
| 494 |             } | 
| 495 |         } | 
| 496 |  | 
| 497 |         return true; | 
| 498 |     } else if (value.structureDefinition().structureType() == QOpcUaStructureDefinition::StructureType::Union) { | 
| 499 |         if (value.fields().size() > 1) { | 
| 500 |             qCWarning(lcGenericStructHandler) << "Multiple union fields were specified, unable to encode" ; | 
| 501 |             return false; | 
| 502 |         } | 
| 503 |  | 
| 504 |         if (value.fields().size() > 0) { | 
| 505 |             for (int i = 0; i < value.structureDefinition().fields().size(); ++i) { | 
| 506 |                 if (value.structureDefinition().fields().at(i).name() == value.fields().keys().first()) { | 
| 507 |                     const auto success = encoder.encode<quint32>(src: 1 << i); | 
| 508 |  | 
| 509 |                     if (!success) { | 
| 510 |                         qCWarning(lcGenericStructHandler) << "Failed to encode union mask" ; | 
| 511 |                         return false; | 
| 512 |                     } | 
| 513 |  | 
| 514 |                     return encodeKnownTypesInternal(encoder, value: value.fields().constKeyValueBegin()->second, | 
| 515 |                                                     valueRank: value.structureDefinition().fields().at(i).valueRank(), | 
| 516 |                                                     dataTypeId: value.structureDefinition().fields().at(i).dataType()); | 
| 517 |                 } | 
| 518 |             } | 
| 519 |  | 
| 520 |             qCWarning(lcGenericStructHandler) << "Unknown union field"  << value.fields().keys().first(); | 
| 521 |             return false; | 
| 522 |         } else { | 
| 523 |             // An empty union consists only of the mask | 
| 524 |             return encoder.encode<quint32>(src: 0); | 
| 525 |         } | 
| 526 |     } else { | 
| 527 |         qCWarning(lcGenericStructHandler) << "Encoding failed, unknown struct type encountered" ; | 
| 528 |         return false; | 
| 529 |     } | 
| 530 |  | 
| 531 |     return false; | 
| 532 | } | 
| 533 |  | 
| 534 | bool QOpcUaGenericStructHandlerPrivate::encodeKnownTypesInternal(QOpcUaBinaryDataEncoding &encoder, const QVariant &value, | 
| 535 |                                                                  qint32 valueRank, const QString &dataTypeId) | 
| 536 | { | 
| 537 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Boolean)) | 
| 538 |         return encodeArrayOrScalar<bool>(encoder, valueRank, value); | 
| 539 |  | 
| 540 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Byte)) | 
| 541 |         return encodeArrayOrScalar<quint8>(encoder, valueRank, value); | 
| 542 |  | 
| 543 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::SByte)) | 
| 544 |         return encodeArrayOrScalar<qint8>(encoder, valueRank, value); | 
| 545 |  | 
| 546 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt16)) | 
| 547 |         return encodeArrayOrScalar<quint16>(encoder, valueRank, value); | 
| 548 |  | 
| 549 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int16)) | 
| 550 |         return encodeArrayOrScalar<qint16>(encoder, valueRank, value); | 
| 551 |  | 
| 552 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt32)) | 
| 553 |         return encodeArrayOrScalar<quint32>(encoder, valueRank, value); | 
| 554 |  | 
| 555 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int32)) | 
| 556 |         return encodeArrayOrScalar<qint32>(encoder, valueRank, value); | 
| 557 |  | 
| 558 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt64)) | 
| 559 |         return encodeArrayOrScalar<quint64>(encoder, valueRank, value); | 
| 560 |  | 
| 561 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int64)) | 
| 562 |         return encodeArrayOrScalar<qint64>(encoder, valueRank, value); | 
| 563 |  | 
| 564 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Float)) | 
| 565 |         return encodeArrayOrScalar<float>(encoder, valueRank, value); | 
| 566 |  | 
| 567 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Double)) | 
| 568 |         return encodeArrayOrScalar<double>(encoder, valueRank, value); | 
| 569 |  | 
| 570 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StatusCode)) | 
| 571 |         return encodeArrayOrScalar<QOpcUa::UaStatusCode>(encoder, valueRank, value); | 
| 572 |  | 
| 573 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DateTime)) | 
| 574 |         return encodeArrayOrScalar<QDateTime>(encoder, valueRank, value); | 
| 575 |  | 
| 576 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::String)) | 
| 577 |         return encodeArrayOrScalar<QString>(encoder, valueRank, value); | 
| 578 |  | 
| 579 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::NodeId)) | 
| 580 |         return encodeArrayOrScalar<QString, QOpcUa::Types::NodeId>(encoder, valueRank, value); | 
| 581 |  | 
| 582 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ByteString)) | 
| 583 |         return encodeArrayOrScalar<QByteArray>(encoder, valueRank, value); | 
| 584 |  | 
| 585 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::XmlElement)) | 
| 586 |         return encodeArrayOrScalar<QByteArray>(encoder, valueRank, value); | 
| 587 |  | 
| 588 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Guid)) | 
| 589 |         return encodeArrayOrScalar<QUuid>(encoder, valueRank, value); | 
| 590 |  | 
| 591 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::QualifiedName)) | 
| 592 |         return encodeArrayOrScalar<QOpcUaQualifiedName>(encoder, valueRank, value); | 
| 593 |  | 
| 594 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::LocalizedText)) | 
| 595 |         return encodeArrayOrScalar<QOpcUaLocalizedText>(encoder, valueRank, value); | 
| 596 |  | 
| 597 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Range)) | 
| 598 |         return encodeArrayOrScalar<QOpcUaRange>(encoder, valueRank, value); | 
| 599 |  | 
| 600 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::EUInformation)) | 
| 601 |         return encodeArrayOrScalar<QOpcUaEUInformation>(encoder, valueRank, value); | 
| 602 |  | 
| 603 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ComplexNumberType)) | 
| 604 |         return encodeArrayOrScalar<QOpcUaComplexNumber>(encoder, valueRank, value); | 
| 605 |  | 
| 606 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DoubleComplexNumberType)) | 
| 607 |         return encodeArrayOrScalar<QOpcUaDoubleComplexNumber>(encoder, valueRank, value); | 
| 608 |  | 
| 609 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::AxisInformation)) | 
| 610 |         return encodeArrayOrScalar<QOpcUaAxisInformation>(encoder, valueRank, value); | 
| 611 |  | 
| 612 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::XVType)) | 
| 613 |         return encodeArrayOrScalar<QOpcUaXValue>(encoder, valueRank, value); | 
| 614 |  | 
| 615 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ExpandedNodeId)) | 
| 616 |         return encodeArrayOrScalar<QOpcUaExpandedNodeId>(encoder, valueRank, value); | 
| 617 |  | 
| 618 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Argument)) | 
| 619 |         return encodeArrayOrScalar<QOpcUaArgument>(encoder, valueRank, value); | 
| 620 |  | 
| 621 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StructureDefinition)) | 
| 622 |         return encodeArrayOrScalar<QOpcUaStructureDefinition>(encoder, valueRank, value); | 
| 623 |  | 
| 624 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StructureField)) | 
| 625 |         return encodeArrayOrScalar<QOpcUaStructureField>(encoder, valueRank, value); | 
| 626 |  | 
| 627 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::EnumDefinition)) | 
| 628 |         return encodeArrayOrScalar<QOpcUaEnumDefinition>(encoder, valueRank, value); | 
| 629 |  | 
| 630 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::EnumField)) | 
| 631 |         return encodeArrayOrScalar<QOpcUaEnumField>(encoder, valueRank, value); | 
| 632 |  | 
| 633 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DiagnosticInfo)) { | 
| 634 |         return encodeArrayOrScalar<QOpcUaDiagnosticInfo>(encoder, valueRank, value); | 
| 635 |     } | 
| 636 |  | 
| 637 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DataValue)) | 
| 638 |         return encodeArrayOrScalar<QOpcUaDataValue>(encoder, valueRank, value); | 
| 639 |  | 
| 640 |     if (dataTypeId == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::BaseDataType)) | 
| 641 |         return encodeArrayOrScalar<QOpcUaVariant>(encoder, valueRank, value); | 
| 642 |  | 
| 643 |     const auto enumType = m_enumsByTypeId.constFind(key: dataTypeId); | 
| 644 |     if (enumType != m_enumsByTypeId.constEnd()) { | 
| 645 |         if (enumType->isAbstract) { | 
| 646 |             qCWarning(lcGenericStructHandler) << "Encoding abstract enum"  << enumType->name << "requested" ; | 
| 647 |             return false; | 
| 648 |         } | 
| 649 |  | 
| 650 |         return encodeArrayOrScalar<qint32>(encoder, valueRank, value); | 
| 651 |     } | 
| 652 |  | 
| 653 |     if (dataTypeKindForTypeId(id: dataTypeId) == QOpcUaGenericStructHandler::DataTypeKind::Struct) { | 
| 654 |         if (valueRank == -1) { | 
| 655 |             if (!value.canConvert<QOpcUaGenericStructValue>()) { | 
| 656 |                 qCWarning(lcGenericStructHandler) << "Struct value expected for member, unable to encode" ; | 
| 657 |                 return false; | 
| 658 |             } | 
| 659 |  | 
| 660 |             const auto genericStruct = value.value<QOpcUaGenericStructValue>(); | 
| 661 |  | 
| 662 |             if (genericStruct.typeId() != dataTypeId) { | 
| 663 |                 qCWarning(lcGenericStructHandler) << "Type mismatch for nested struct value, unable to encode" ; | 
| 664 |                 return false; | 
| 665 |             } | 
| 666 |  | 
| 667 |             return encodeStructInternal(encoder, value: genericStruct); | 
| 668 |         } else if (valueRank == 1) { | 
| 669 |             if (!value.canConvert<QList<QOpcUaGenericStructValue>>()) { | 
| 670 |                 qCWarning(lcGenericStructHandler) << "Struct list value expected for member, unable to encode" ; | 
| 671 |                 return false; | 
| 672 |             } | 
| 673 |  | 
| 674 |             const auto data = value.value<QList<QOpcUaGenericStructValue>>(); | 
| 675 |  | 
| 676 |             auto success = encoder.encode<qint32>(src: data.size()); | 
| 677 |  | 
| 678 |             if (!success) { | 
| 679 |                 qCWarning(lcGenericStructHandler) << "Failed to encode array length" ; | 
| 680 |                 return false; | 
| 681 |             } | 
| 682 |  | 
| 683 |             for (const auto &entry : data) { | 
| 684 |                 success = encodeStructInternal(encoder, value: entry); | 
| 685 |  | 
| 686 |                 if (!success) { | 
| 687 |                     qCWarning(lcGenericStructHandler) << "Failed to encode struct into the array" ; | 
| 688 |                     return false; | 
| 689 |                 } | 
| 690 |             } | 
| 691 |  | 
| 692 |             return true; | 
| 693 |         } else if (valueRank > 1) { | 
| 694 |             if (!value.canConvert<QOpcUaMultiDimensionalArray>()) { | 
| 695 |                 qCWarning(lcGenericStructHandler) << "QOpcUaMultiDimensionalArray value expected for member, unable to encode" ; | 
| 696 |                 return false; | 
| 697 |             } | 
| 698 |  | 
| 699 |             const auto array = value.value<QOpcUaMultiDimensionalArray>(); | 
| 700 |  | 
| 701 |             for (const auto &entry : array.valueArray()) { | 
| 702 |                 if (!entry.canConvert<QOpcUaGenericStructValue>()) { | 
| 703 |                     qCWarning(lcGenericStructHandler) << "QOpcUaMultiDimensionalArray value is expected to contain"  | 
| 704 |                                                          << "a generic struct, unable to encode" ; | 
| 705 |                     return false; | 
| 706 |                 } | 
| 707 |             } | 
| 708 |  | 
| 709 |             auto success = encoder.encodeArray<quint32>(src: array.arrayDimensions()); | 
| 710 |  | 
| 711 |             if (!success) { | 
| 712 |                 qCWarning(lcGenericStructHandler) << "Failed to encode array dimensions" ; | 
| 713 |                 return false; | 
| 714 |             } | 
| 715 |  | 
| 716 |             success = encoder.encode<qint32>(src: array.valueArray().size()); | 
| 717 |  | 
| 718 |             if (!success) { | 
| 719 |                 qCWarning(lcGenericStructHandler) << "Failed to encode array length" ; | 
| 720 |                 return false; | 
| 721 |             } | 
| 722 |  | 
| 723 |             for (const auto &entry : array.valueArray()) { | 
| 724 |                 success = encodeStructInternal(encoder, value: entry.value<QOpcUaGenericStructValue>()); | 
| 725 |  | 
| 726 |                 if (!success) { | 
| 727 |                     qCWarning(lcGenericStructHandler) << "Failed to encode struct into the array" ; | 
| 728 |                     return false; | 
| 729 |                 } | 
| 730 |             } | 
| 731 |  | 
| 732 |             return true; | 
| 733 |         } | 
| 734 |  | 
| 735 |         return false; | 
| 736 |     } | 
| 737 |  | 
| 738 |     // Maybe this is a supertype of a built-in type | 
| 739 |     const auto superType = m_knownSubtypes.constFind(key: dataTypeId); | 
| 740 |     if (superType != m_knownSubtypes.constEnd()) | 
| 741 |         return encodeKnownTypesInternal(encoder, value, valueRank, dataTypeId: superType.value()); | 
| 742 |  | 
| 743 |     return false; | 
| 744 | } | 
| 745 |  | 
| 746 | void QOpcUaGenericStructHandlerPrivate::handleFinished(bool success) | 
| 747 | { | 
| 748 |     if (m_hasError) | 
| 749 |         return; | 
| 750 |  | 
| 751 |     m_finishedCount++; | 
| 752 |     m_hasError = !success; | 
| 753 |  | 
| 754 |     Q_Q(QOpcUaGenericStructHandler); | 
| 755 |  | 
| 756 |     if (m_finishedCount == 1 || m_hasError) { | 
| 757 |         m_initialized = !m_hasError; | 
| 758 |         emit q->initializedChanged(initialized: m_initialized); | 
| 759 |     } | 
| 760 | } | 
| 761 |  | 
| 762 | bool QOpcUaGenericStructHandlerPrivate::addCustomStructureDefinition(const QOpcUaStructureDefinition &definition, | 
| 763 |                                                                      const QString &typeId, const QString &name, QOpcUa::IsAbstract isAbstract) | 
| 764 | { | 
| 765 |     if (typeId.isEmpty()) { | 
| 766 |         qCWarning(lcGenericStructHandler) << "Failed to add custom structure definition, typeId must not be empty" ; | 
| 767 |         return false; | 
| 768 |     } | 
| 769 |  | 
| 770 |     if (name.isEmpty()) { | 
| 771 |         qCWarning(lcGenericStructHandler) << "Failed to add custom structure definition, name must not be empty" ; | 
| 772 |         return false; | 
| 773 |     } | 
| 774 |  | 
| 775 |     if (definition.defaultEncodingId().isEmpty()) { | 
| 776 |         qCWarning(lcGenericStructHandler) << "Failed to add custom structure definition, definition.defaultEncodingId() must not be empty" ; | 
| 777 |         return false; | 
| 778 |     } | 
| 779 |  | 
| 780 |     StructMapEntry entry; | 
| 781 |     entry.isAbstract = isAbstract == QOpcUa::IsAbstract::Abstract; | 
| 782 |     entry.name = name; | 
| 783 |     entry.nodeId = typeId; | 
| 784 |     entry.structureDefinition = definition; | 
| 785 |  | 
| 786 |     m_structuresByTypeId[typeId] = entry; | 
| 787 |     m_structuresByEncodingId[definition.defaultEncodingId()] = entry; | 
| 788 |     m_typeNamesByEncodingId[definition.defaultEncodingId()] = name; | 
| 789 |     m_typeNamesByTypeId[typeId] = name; | 
| 790 |  | 
| 791 |     if (entry.isAbstract) | 
| 792 |         m_abstractTypeIds.insert(value: typeId); | 
| 793 |  | 
| 794 |     return true; | 
| 795 | } | 
| 796 |  | 
| 797 | bool QOpcUaGenericStructHandlerPrivate::addCustomEnumDefinition(const QOpcUaEnumDefinition &definition, | 
| 798 |                                                                 const QString &typeId, const QString &name, | 
| 799 |                                                                 QOpcUa::IsAbstract isAbstract) | 
| 800 | { | 
| 801 |     if (typeId.isEmpty()) { | 
| 802 |         qCWarning(lcGenericStructHandler) << "Failed to add custom enum definition, typeId must not be empty" ; | 
| 803 |         return false; | 
| 804 |     } | 
| 805 |  | 
| 806 |     if (name.isEmpty()) { | 
| 807 |         qCWarning(lcGenericStructHandler) << "Failed to add custom enum definition, name must not be empty" ; | 
| 808 |         return false; | 
| 809 |     } | 
| 810 |  | 
| 811 |     EnumMapEntry entry; | 
| 812 |     entry.isAbstract = isAbstract == QOpcUa::IsAbstract::Abstract; | 
| 813 |     entry.name = name; | 
| 814 |     entry.nodeId = typeId; | 
| 815 |     entry.enumDefinition = definition; | 
| 816 |  | 
| 817 |     m_enumsByTypeId[typeId] = entry; | 
| 818 |     m_typeNamesByTypeId[typeId] = name; | 
| 819 |  | 
| 820 |     return true; | 
| 821 | } | 
| 822 |  | 
| 823 | bool QOpcUaGenericStructHandlerPrivate::initialized() const | 
| 824 | { | 
| 825 |     return m_initialized; | 
| 826 | } | 
| 827 |  | 
| 828 | void QOpcUaGenericStructHandlerPrivate::handleInitializeFinished(bool success) | 
| 829 | { | 
| 830 |     if (!success) { | 
| 831 |         qCWarning(lcGenericStructHandler) << "Failed to read the data type tree" ; | 
| 832 |         handleFinished(success: false); | 
| 833 |         return; | 
| 834 |     } else { | 
| 835 |         processDataTypeRecursive(node: m_baseDataType.get()); | 
| 836 |         handleFinished(success: true); | 
| 837 |     } | 
| 838 | } | 
| 839 |  | 
| 840 | void QOpcUaGenericStructHandlerPrivate::processDataTypeRecursive(QOpcUaInternalDataTypeNode *node) | 
| 841 | { | 
| 842 |     qCDebug(lcGenericStructHandler) << "Found DataType:"  << node->name(); | 
| 843 |  | 
| 844 |     m_typeNamesByTypeId[node->nodeId()] = node->name(); | 
| 845 |     if (node->isAbstract()) | 
| 846 |         m_abstractTypeIds.insert(value: node->nodeId()); | 
| 847 |  | 
| 848 |     for (const auto &child : node->children()) { | 
| 849 |         if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Structure)) | 
| 850 |             processStructRecursive(node: child.get()); | 
| 851 |         else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Enumeration)) | 
| 852 |             processEnumRecursive(node: child.get()); | 
| 853 |         else { | 
| 854 |             if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Boolean)) | 
| 855 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 856 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int32)) | 
| 857 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 858 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt32)) | 
| 859 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 860 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Double)) | 
| 861 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 862 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Float)) | 
| 863 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 864 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::String)) | 
| 865 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 866 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::LocalizedText)) | 
| 867 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 868 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::DateTime)) | 
| 869 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 870 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt16)) | 
| 871 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 872 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int16)) | 
| 873 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 874 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::UInt64)) | 
| 875 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 876 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Int64)) | 
| 877 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 878 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Byte)) | 
| 879 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 880 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::SByte)) | 
| 881 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 882 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::ByteString)) | 
| 883 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 884 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::XmlElement)) | 
| 885 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 886 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::NodeId)) | 
| 887 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 888 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::Guid)) | 
| 889 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 890 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::QualifiedName)) | 
| 891 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 892 |             else if (child->nodeId() == QOpcUa::namespace0Id(id: QOpcUa::NodeIds::Namespace0::StatusCode)) | 
| 893 |                 processSubtypeOfKnownTypeRecursive(node: child.get(), id: child->nodeId()); | 
| 894 |             else | 
| 895 |                 processDataTypeRecursive(node: child.get()); | 
| 896 |         } | 
| 897 |     } | 
| 898 | } | 
| 899 |  | 
| 900 | void QOpcUaGenericStructHandlerPrivate::processStructRecursive(QOpcUaInternalDataTypeNode *node) | 
| 901 | { | 
| 902 |     qCDebug(lcGenericStructHandler) << "Found struct:"  << node->name(); | 
| 903 |  | 
| 904 |     m_typeNamesByTypeId.insert(key: node->nodeId(), value: node->name()); | 
| 905 |  | 
| 906 |     if (node->isAbstract()) | 
| 907 |         m_abstractTypeIds.insert(value: node->nodeId()); | 
| 908 |  | 
| 909 |     const auto structureDefinition = node->definition().value<QOpcUaStructureDefinition>(); | 
| 910 |     if (!structureDefinition.defaultEncodingId().isEmpty()) | 
| 911 |         m_typeNamesByEncodingId.insert(key: structureDefinition.defaultEncodingId(), value: node->name()); | 
| 912 |  | 
| 913 |     const StructMapEntry entry{ .name: node->name(), .nodeId: node->nodeId(), .isAbstract: node->isAbstract(), .structureDefinition: structureDefinition }; | 
| 914 |     m_structuresByEncodingId.insert(key: structureDefinition.defaultEncodingId(), value: entry); | 
| 915 |     m_structuresByTypeId.insert(key: node->nodeId(), value: entry); | 
| 916 |  | 
| 917 |     for (const auto &child : node->children()) | 
| 918 |         processStructRecursive(node: child.get()); | 
| 919 | } | 
| 920 |  | 
| 921 | void QOpcUaGenericStructHandlerPrivate::processEnumRecursive(QOpcUaInternalDataTypeNode *node) | 
| 922 | { | 
| 923 |     qCDebug(lcGenericStructHandler) << "Found enum:"  << node->name(); | 
| 924 |  | 
| 925 |     m_typeNamesByTypeId[node->nodeId()] = node->name(); | 
| 926 |  | 
| 927 |     if (node->isAbstract()) | 
| 928 |         m_abstractTypeIds.insert(value: node->nodeId()); | 
| 929 |  | 
| 930 |     EnumMapEntry entry; | 
| 931 |     entry.name = node->name(); | 
| 932 |     entry.enumDefinition = node->definition().value<QOpcUaEnumDefinition>(); | 
| 933 |     entry.nodeId = node->nodeId(); | 
| 934 |     entry.isAbstract = node->isAbstract(); | 
| 935 |     m_enumsByTypeId.insert(key: node->nodeId(), value: entry); | 
| 936 |  | 
| 937 |     for (const auto &child : node->children()) | 
| 938 |         processEnumRecursive(node: child.get()); | 
| 939 | } | 
| 940 |  | 
| 941 | void QOpcUaGenericStructHandlerPrivate::processSubtypeOfKnownTypeRecursive(QOpcUaInternalDataTypeNode *node, const QString &id) | 
| 942 | { | 
| 943 |     m_typeNamesByTypeId[node->nodeId()] = node->name(); | 
| 944 |  | 
| 945 |     if (node->isAbstract()) | 
| 946 |         m_abstractTypeIds.insert(value: node->nodeId()); | 
| 947 |  | 
| 948 |     if (node->nodeId() != id) | 
| 949 |         m_knownSubtypes[node->nodeId()] = id; | 
| 950 |  | 
| 951 |     for (const auto &child : node->children()) | 
| 952 |         processSubtypeOfKnownTypeRecursive(node: child.get(), id); | 
| 953 | } | 
| 954 |  | 
| 955 | QT_END_NAMESPACE | 
| 956 |  |