Revision control
++ // Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "private/qopcuagenericstructhandler_p.h"
++
++ #include <QtOpcUa/qopcuaargument.h>
++ #include <QtOpcUa/qopcuaaxisinformation.h>
++ #include <QtOpcUa/qopcuabinarydataencoding.h>
++ #include <QtOpcUa/qopcuacomplexnumber.h>
++ #include <QtOpcUa/qopcuadatavalue.h>
++ #include <QtOpcUa/qopcuadiagnosticinfo.h>
++ #include <QtOpcUa/qopcuadoublecomplexnumber.h>
++ #include <QtOpcUa/qopcuaenumfield.h>
++ #include <QtOpcUa/qopcuaeuinformation.h>
++ #include <QtOpcUa/qopcuagenericstructhandler.h>
++ #include <QtOpcUa/qopcuagenericstructvalue.h>
++ #include <QtOpcUa/qopcuarange.h>
++ #include <QtOpcUa/qopcuastructurefield.h>
++ #include <QtOpcUa/qopcuaxvalue.h>
++
++ #include "qopcuainternaldatatypenode_p.h"
++
++ #include <QtCore/quuid.h>
++
++ QT_BEGIN_NAMESPACE
++
++ Q_LOGGING_CATEGORY(lcGenericStructHandler, "qt.opcuagenericstructhandler")
++
++ QOpcUaGenericStructHandlerPrivate::QOpcUaGenericStructHandlerPrivate(QOpcUaClient *client)
++ : m_client(client)
++ {
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::initialize()
++ {
++ if (!m_client)
++ return false;
++
++ m_initialized = false;
++
++ m_baseDataType.reset(new QOpcUaInternalDataTypeNode(m_client));
++
++ QObjectPrivate::connect(m_baseDataType.get(), &QOpcUaInternalDataTypeNode::initializeFinished,
++ this, &QOpcUaGenericStructHandlerPrivate::handleInitializeFinished);
++
++ return m_baseDataType->initialize(QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::BaseDataType));
++ }
++
++ QOpcUaGenericStructValue QOpcUaGenericStructHandlerPrivate::decode(const QOpcUaExtensionObject &extensionObject, bool &success) const
++ {
++ const auto entry = m_structuresByEncodingId.constFind(extensionObject.encodingTypeId());
++ if (entry == m_structuresByEncodingId.constEnd()) {
++ qCWarning(lcGenericStructHandler) << "Failed to find description for" << extensionObject.encodingTypeId();
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ qCDebug(lcGenericStructHandler) << "Decoding" << entry->name << entry.key();
++
++ auto data = extensionObject.encodedBody();
++ QOpcUaBinaryDataEncoding decoder(&data);
++
++ return decodeStructInternal(decoder, entry->nodeId, success, 0);
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::encode(const QOpcUaGenericStructValue &value, QOpcUaExtensionObject &output)
++ {
++ if (value.structureDefinition().fields().empty()) {
++ qCWarning(lcGenericStructHandler) << "The structure doesn't have any fields, unable to encode";
++ return false;
++ }
++
++ if (value.structureDefinition().defaultEncodingId().isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "The struct doesn't have an encoding ID, unable to encode";
++ return false;
++ }
++
++ output.setEncodingTypeId(value.structureDefinition().defaultEncodingId());
++ output.setEncoding(QOpcUaExtensionObject::Encoding::ByteString);
++
++ QOpcUaBinaryDataEncoding encoder(output);
++ return encodeStructInternal(encoder, value);
++ }
++
++ QOpcUaGenericStructValue QOpcUaGenericStructHandlerPrivate::createGenericStructValueForTypeId(const QString &typeId)
++ {
++ const auto entry = m_structuresByTypeId.constFind(typeId);
++
++ if (entry != m_structuresByTypeId.constEnd()) {
++ QOpcUaGenericStructValue value(entry.value().name, typeId, entry.value().structureDefinition);
++
++ if (entry.value().structureDefinition.structureType() != QOpcUaStructureDefinition::StructureType::Union) {
++ const auto tempFields = entry.value().structureDefinition.fields();
++ for (const auto &field : tempFields) {
++ if (field.isOptional())
++ continue;
++
++ value.fieldsRef().insert(field.name(), QVariant());
++ }
++ }
++
++ return value;
++ }
++
++ qCWarning(lcGenericStructHandler) << "Unable to create a pre-filled generic struct value for" << typeId;
++ return {};
++ }
++
++ QOpcUaStructureDefinition QOpcUaGenericStructHandlerPrivate::structureDefinitionForBinaryEncodingId(const QString &id) const
++ {
++ const auto entry = m_structuresByEncodingId.constFind(id);
++ return entry != m_structuresByEncodingId.constEnd() ? entry.value().structureDefinition : QOpcUaStructureDefinition();
++ }
++
++ QOpcUaStructureDefinition QOpcUaGenericStructHandlerPrivate::structureDefinitionForTypeId(const QString &id) const
++ {
++ const auto entry = m_structuresByTypeId.constFind(id);
++ return entry != m_structuresByTypeId.constEnd() ? entry.value().structureDefinition : QOpcUaStructureDefinition();
++ }
++
++ QOpcUaEnumDefinition QOpcUaGenericStructHandlerPrivate::enumDefinitionForTypeId(const QString &id) const
++ {
++ const auto entry = m_enumsByTypeId.constFind(id);
++ return entry != m_enumsByTypeId.constEnd() ? entry.value().enumDefinition : QOpcUaEnumDefinition();
++ }
++
++ QString QOpcUaGenericStructHandlerPrivate::typeNameForBinaryEncodingId(const QString &id) const
++ {
++ const auto entry = m_typeNamesByEncodingId.constFind(id);
++ return entry != m_typeNamesByEncodingId.constEnd() ? entry.value() : QString();
++ }
++
++ QString QOpcUaGenericStructHandlerPrivate::typeNameForTypeId(const QString &id) const
++ {
++ const auto entry = m_typeNamesByTypeId.constFind(id);
++ return entry != m_typeNamesByTypeId.constEnd() ? entry.value() : QString();
++ }
++
++ QOpcUaGenericStructHandler::DataTypeKind QOpcUaGenericStructHandlerPrivate::dataTypeKindForTypeId(const QString &id) const
++ {
++ const auto isStruct = m_structuresByTypeId.constFind(id);
++ if (isStruct != m_structuresByTypeId.constEnd())
++ return QOpcUaGenericStructHandler::DataTypeKind::Struct;
++
++ const auto isEnum = m_enumsByTypeId.constFind(id);
++ if (isEnum != m_enumsByTypeId.constEnd())
++ return QOpcUaGenericStructHandler::DataTypeKind::Enum;
++
++ return typeNameForTypeId(id).isEmpty() ?
++ QOpcUaGenericStructHandler::DataTypeKind::Unknown : QOpcUaGenericStructHandler::DataTypeKind::Other;
++ }
++
++ QString QOpcUaGenericStructHandlerPrivate::typeIdForBinaryEncodingId(const QString &id) const
++ {
++ const auto entry = m_structuresByEncodingId.constFind(id);
++ return entry != m_structuresByEncodingId.constEnd() ? entry->nodeId : QString();
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::isAbstractTypeId(const QString &id) const
++ {
++ return m_abstractTypeIds.contains(id);
++ }
++
++ QOpcUaGenericStructValue QOpcUaGenericStructHandlerPrivate::decodeStructInternal(QOpcUaBinaryDataEncoding &decoder,
++ const QString &dataTypeId, bool &success,
++ int currentDepth) const
++ {
++ if (currentDepth > m_maxNestingLevel) {
++ qCWarning(lcGenericStructHandler) << "Maximum nesting level of" << m_maxNestingLevel << "exceeded";
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ const auto entry = m_structuresByTypeId.constFind(dataTypeId);
++ if (entry == m_structuresByTypeId.constEnd()) {
++ qCWarning(lcGenericStructHandler) << "Failed to find description for" << dataTypeId;
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ if (entry->isAbstract) {
++ qCWarning(lcGenericStructHandler) << "Decoding of abstract struct" << entry->name << "requested";
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ if (entry->structureDefinition.fields().isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Missing fields information for struct" << entry->name;
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ QOpcUaGenericStructValue result(entry->name, entry->nodeId, entry->structureDefinition);
++ auto &fields = result.fieldsRef();
++
++ const auto definitionFields = entry->structureDefinition.fields();
++
++ if (entry->structureDefinition.structureType() == QOpcUaStructureDefinition::StructureType::Structure) {
++ for (const auto &field : definitionFields) {
++ fields[field.name()] = decodeKnownTypesInternal(decoder, field.dataType(), field.valueRank(), success, currentDepth + 1);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode struct field";
++ return QOpcUaGenericStructValue();
++ }
++ }
++ } else if (entry->structureDefinition.structureType() == QOpcUaStructureDefinition::StructureType::Union) {
++ auto switchField = decoder.decode<quint32>(success);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode the union switch field";
++ return QOpcUaGenericStructValue();
++ }
++
++ if (!switchField)
++ return result; // Empty union, no need to continue processing
++
++ if (switchField > static_cast<quint32>(definitionFields.size())) {
++ qCWarning(lcGenericStructHandler) << "Union switch field out of bounds";
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ qCDebug(lcGenericStructHandler) << "Decode union field with switch value" << switchField;
++
++ auto field = definitionFields.at(switchField - 1);
++
++ fields[field.name()] = decodeKnownTypesInternal(decoder, field.dataType(), field.valueRank() > 0, success, currentDepth + 1);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode union content";
++ return QOpcUaGenericStructValue();
++ }
++ } else if (entry->structureDefinition.structureType() == QOpcUaStructureDefinition::StructureType::StructureWithOptionalFields) {
++ auto mask = decoder.decode<quint32>(success);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode the optional fields mask";
++ return QOpcUaGenericStructValue();
++ }
++
++ int optionalFieldIndex = 0;
++ for (const auto &field : definitionFields) {
++ if (field.isOptional() && !(mask & (1 << optionalFieldIndex++)))
++ continue;
++
++ fields[field.name()] = decodeKnownTypesInternal(decoder, field.dataType(), field.valueRank() > 0, success, currentDepth + 1);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode struct field";
++ return QOpcUaGenericStructValue();
++ }
++ }
++ }
++
++ return result;
++ }
++
++ QVariant QOpcUaGenericStructHandlerPrivate::decodeKnownTypesInternal(QOpcUaBinaryDataEncoding &decoder, const QString &dataTypeId,
++ qint32 valueRank, bool &success, int currentDepth) const
++ {
++ if (currentDepth > m_maxNestingLevel) {
++ qCWarning(lcGenericStructHandler) << "Maximum nesting level of" << m_maxNestingLevel << "exceeded";
++ success = false;
++ return QOpcUaGenericStructValue();
++ }
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Boolean))
++ return decodeArrayOrScalar<bool>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Byte))
++ return decodeArrayOrScalar<quint8>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::SByte))
++ return decodeArrayOrScalar<qint8>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt16))
++ return decodeArrayOrScalar<quint16>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int16))
++ return decodeArrayOrScalar<qint16>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt32))
++ return decodeArrayOrScalar<quint32>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int32))
++ return decodeArrayOrScalar<qint32>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt64))
++ return decodeArrayOrScalar<quint64>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int64))
++ return decodeArrayOrScalar<qint64>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Float))
++ return decodeArrayOrScalar<float>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Double))
++ return decodeArrayOrScalar<double>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StatusCode))
++ return decodeArrayOrScalar<QOpcUa::UaStatusCode>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DateTime))
++ return decodeArrayOrScalar<QDateTime>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::String))
++ return decodeArrayOrScalar<QString>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::NodeId))
++ return decodeArrayOrScalar<QString, QOpcUa::Types::NodeId>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ByteString))
++ return decodeArrayOrScalar<QByteArray>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::XmlElement))
++ return decodeArrayOrScalar<QByteArray>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Guid))
++ return decodeArrayOrScalar<QUuid>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::QualifiedName))
++ return decodeArrayOrScalar<QOpcUaQualifiedName>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::LocalizedText))
++ return decodeArrayOrScalar<QOpcUaLocalizedText>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Range))
++ return decodeArrayOrScalar<QOpcUaRange>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::EUInformation))
++ return decodeArrayOrScalar<QOpcUaEUInformation>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ComplexNumberType))
++ return decodeArrayOrScalar<QOpcUaComplexNumber>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DoubleComplexNumberType))
++ return decodeArrayOrScalar<QOpcUaDoubleComplexNumber>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::AxisInformation))
++ return decodeArrayOrScalar<QOpcUaAxisInformation>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::XVType))
++ return decodeArrayOrScalar<QOpcUaXValue>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ExpandedNodeId))
++ return decodeArrayOrScalar<QOpcUaExpandedNodeId>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Argument))
++ return decodeArrayOrScalar<QOpcUaArgument>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StructureDefinition))
++ return decodeArrayOrScalar<QOpcUaStructureDefinition>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StructureField))
++ return decodeArrayOrScalar<QOpcUaStructureField>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::EnumDefinition))
++ return decodeArrayOrScalar<QOpcUaEnumDefinition>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::EnumField))
++ return decodeArrayOrScalar<QOpcUaEnumField>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DiagnosticInfo)) {
++ return decodeArrayOrScalar<QOpcUaDiagnosticInfo>(decoder, valueRank, success);
++ }
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DataValue))
++ return decodeArrayOrScalar<QOpcUaDataValue>(decoder, valueRank, success);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::BaseDataType))
++ return decodeArrayOrScalar<QOpcUaVariant>(decoder, valueRank, success);
++
++ // Enumeration
++ const auto enumType = m_enumsByTypeId.constFind(dataTypeId);
++ if (enumType != m_enumsByTypeId.constEnd()) {
++ if (enumType->isAbstract) {
++ qCWarning(lcGenericStructHandler) << "Decoding abstract enum" << enumType->name << "requested";
++ success = false;
++ return QVariant();
++ }
++
++ const auto enumValue = decodeArrayOrScalar<qint32>(decoder, valueRank, success);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode enum";
++ return QVariant();
++ }
++ return enumValue;
++ }
++
++ // Fallback for nested unknown structs
++ if (valueRank > 0) {
++ QList<quint32> arrayDimensions;
++ if (valueRank > 1) {
++ arrayDimensions = decoder.decodeArray<quint32>(success);
++
++ if (!success)
++ return QVariant();
++ }
++
++ const auto arrayLength = decoder.decode<qint32>(success);
++ if (!success)
++ return QVariant();
++
++ QList<QOpcUaGenericStructValue> result;
++ for (int i = 0; i < arrayLength; ++i) {
++ result.append(decodeStructInternal(decoder, dataTypeId, success, currentDepth + 1));
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode nested struct array";
++ return QVariant();
++ }
++ }
++
++ if (!arrayDimensions.isEmpty()) {
++ QVariantList data(result.constBegin(), result.constEnd());
++ return QOpcUaMultiDimensionalArray(data, arrayDimensions);
++ }
++
++ return QVariant::fromValue(result);
++ } else {
++ const auto result = decodeStructInternal(decoder, dataTypeId, success, currentDepth + 1);
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to decode nested struct";
++ return QVariant();
++ }
++ return result;
++ }
++
++ const auto superType = m_knownSubtypes.constFind(dataTypeId);
++ if (superType != m_knownSubtypes.constEnd())
++ return decodeKnownTypesInternal(decoder, superType.value(), valueRank, success, currentDepth + 1);
++
++ success = false;
++ return QVariant();
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::encodeStructInternal(QOpcUaBinaryDataEncoding &encoder, const QOpcUaGenericStructValue &value)
++ {
++ const auto entry = m_structuresByTypeId.constFind(value.typeId());
++
++ if (entry == m_structuresByTypeId.constEnd()) {
++ qCWarning(lcGenericStructHandler) << "Failed to find description for" << value.typeId();
++ return false;
++ }
++
++ if (entry->isAbstract) {
++ qCWarning(lcGenericStructHandler) << "Decoding of abstract struct" << entry->name << "requested";
++ return false;
++ }
++
++ if (entry->structureDefinition.fields().isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Missing fields information for struct" << entry->name;
++ return false;
++ }
++
++ const auto definitionFields = value.structureDefinition().fields();
++
++ if (value.structureDefinition().structureType() == QOpcUaStructureDefinition::StructureType::Structure) {
++ for (const auto &field : definitionFields) {
++ if (!value.fields().contains(field.name())) {
++ qCWarning(lcGenericStructHandler) << "Field" << field.name() << "is missing, unable to encode struct";
++ return false;
++ }
++ const auto success = encodeKnownTypesInternal(encoder, value.fields().value(field.name()), field.valueRank(), field.dataType());
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode struct field" << field.name();
++ return false;
++ }
++ }
++
++ return true;
++ } else if (value.structureDefinition().structureType() == QOpcUaStructureDefinition::StructureType::StructureWithOptionalFields) {
++ quint32 mask = 0;
++ quint32 index = 0;
++ for (const auto &field : definitionFields) {
++ if (!field.isOptional())
++ continue;
++
++ if (value.fields().contains(field.name()))
++ mask |= (1 << index);
++
++ ++index;
++ }
++
++ auto success = encoder.encode<quint32>(mask);
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode optional fields mask";
++ return false;
++ }
++
++ for (const auto &field : definitionFields) {
++ if (!field.isOptional() && !value.fields().contains(field.name())) {
++ qCWarning(lcGenericStructHandler) << "Field" << field.name() << "is missing, unable to encode struct";
++ return false;
++ }
++
++ if (value.fields().contains(field.name())) {
++ const auto success = encodeKnownTypesInternal(encoder, value.fields().value(field.name()), field.valueRank(), field.dataType());
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode struct field" << field.name();
++ return false;
++ }
++ }
++ }
++
++ return true;
++ } else if (value.structureDefinition().structureType() == QOpcUaStructureDefinition::StructureType::Union) {
++ if (value.fields().size() > 1) {
++ qCWarning(lcGenericStructHandler) << "Multiple union fields were specified, unable to encode";
++ return false;
++ }
++
++ if (value.fields().size() > 0) {
++ for (int i = 0; i < definitionFields.size(); ++i) {
++ if (definitionFields.at(i).name() == value.fields().keys().first()) {
++ const auto success = encoder.encode<quint32>(1 << i);
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode union mask";
++ return false;
++ }
++
++ return encodeKnownTypesInternal(encoder, value.fields().constKeyValueBegin()->second,
++ definitionFields.at(i).valueRank(),
++ definitionFields.at(i).dataType());
++ }
++ }
++
++ qCWarning(lcGenericStructHandler) << "Unknown union field" << value.fields().keys().first();
++ return false;
++ } else {
++ // An empty union consists only of the mask
++ return encoder.encode<quint32>(0);
++ }
++ } else {
++ qCWarning(lcGenericStructHandler) << "Encoding failed, unknown struct type encountered";
++ return false;
++ }
++
++ return false;
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::encodeKnownTypesInternal(QOpcUaBinaryDataEncoding &encoder, const QVariant &value,
++ qint32 valueRank, const QString &dataTypeId)
++ {
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Boolean))
++ return encodeArrayOrScalar<bool>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Byte))
++ return encodeArrayOrScalar<quint8>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::SByte))
++ return encodeArrayOrScalar<qint8>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt16))
++ return encodeArrayOrScalar<quint16>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int16))
++ return encodeArrayOrScalar<qint16>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt32))
++ return encodeArrayOrScalar<quint32>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int32))
++ return encodeArrayOrScalar<qint32>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt64))
++ return encodeArrayOrScalar<quint64>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int64))
++ return encodeArrayOrScalar<qint64>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Float))
++ return encodeArrayOrScalar<float>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Double))
++ return encodeArrayOrScalar<double>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StatusCode))
++ return encodeArrayOrScalar<QOpcUa::UaStatusCode>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DateTime))
++ return encodeArrayOrScalar<QDateTime>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::String))
++ return encodeArrayOrScalar<QString>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::NodeId))
++ return encodeArrayOrScalar<QString, QOpcUa::Types::NodeId>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ByteString))
++ return encodeArrayOrScalar<QByteArray>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::XmlElement))
++ return encodeArrayOrScalar<QByteArray>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Guid))
++ return encodeArrayOrScalar<QUuid>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::QualifiedName))
++ return encodeArrayOrScalar<QOpcUaQualifiedName>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::LocalizedText))
++ return encodeArrayOrScalar<QOpcUaLocalizedText>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Range))
++ return encodeArrayOrScalar<QOpcUaRange>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::EUInformation))
++ return encodeArrayOrScalar<QOpcUaEUInformation>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ComplexNumberType))
++ return encodeArrayOrScalar<QOpcUaComplexNumber>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DoubleComplexNumberType))
++ return encodeArrayOrScalar<QOpcUaDoubleComplexNumber>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::AxisInformation))
++ return encodeArrayOrScalar<QOpcUaAxisInformation>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::XVType))
++ return encodeArrayOrScalar<QOpcUaXValue>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ExpandedNodeId))
++ return encodeArrayOrScalar<QOpcUaExpandedNodeId>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Argument))
++ return encodeArrayOrScalar<QOpcUaArgument>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StructureDefinition))
++ return encodeArrayOrScalar<QOpcUaStructureDefinition>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StructureField))
++ return encodeArrayOrScalar<QOpcUaStructureField>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::EnumDefinition))
++ return encodeArrayOrScalar<QOpcUaEnumDefinition>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::EnumField))
++ return encodeArrayOrScalar<QOpcUaEnumField>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DiagnosticInfo)) {
++ return encodeArrayOrScalar<QOpcUaDiagnosticInfo>(encoder, valueRank, value);
++ }
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DataValue))
++ return encodeArrayOrScalar<QOpcUaDataValue>(encoder, valueRank, value);
++
++ if (dataTypeId == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::BaseDataType))
++ return encodeArrayOrScalar<QOpcUaVariant>(encoder, valueRank, value);
++
++ const auto enumType = m_enumsByTypeId.constFind(dataTypeId);
++ if (enumType != m_enumsByTypeId.constEnd()) {
++ if (enumType->isAbstract) {
++ qCWarning(lcGenericStructHandler) << "Encoding abstract enum" << enumType->name << "requested";
++ return false;
++ }
++
++ return encodeArrayOrScalar<qint32>(encoder, valueRank, value);
++ }
++
++ if (dataTypeKindForTypeId(dataTypeId) == QOpcUaGenericStructHandler::DataTypeKind::Struct) {
++ if (valueRank == -1) {
++ if (!value.canConvert<QOpcUaGenericStructValue>()) {
++ qCWarning(lcGenericStructHandler) << "Struct value expected for member, unable to encode";
++ return false;
++ }
++
++ const auto genericStruct = value.value<QOpcUaGenericStructValue>();
++
++ if (genericStruct.typeId() != dataTypeId) {
++ qCWarning(lcGenericStructHandler) << "Type mismatch for nested struct value, unable to encode";
++ return false;
++ }
++
++ return encodeStructInternal(encoder, genericStruct);
++ } else if (valueRank == 1) {
++ if (!value.canConvert<QList<QOpcUaGenericStructValue>>()) {
++ qCWarning(lcGenericStructHandler) << "Struct list value expected for member, unable to encode";
++ return false;
++ }
++
++ const auto data = value.value<QList<QOpcUaGenericStructValue>>();
++
++ auto success = encoder.encode<qint32>(data.size());
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode array length";
++ return false;
++ }
++
++ for (const auto &entry : data) {
++ success = encodeStructInternal(encoder, entry);
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode struct into the array";
++ return false;
++ }
++ }
++
++ return true;
++ } else if (valueRank > 1) {
++ if (!value.canConvert<QOpcUaMultiDimensionalArray>()) {
++ qCWarning(lcGenericStructHandler) << "QOpcUaMultiDimensionalArray value expected for member, unable to encode";
++ return false;
++ }
++
++ const auto array = value.value<QOpcUaMultiDimensionalArray>();
++
++ const auto valueArray = array.valueArray();
++
++ for (const auto &entry : valueArray) {
++ if (!entry.canConvert<QOpcUaGenericStructValue>()) {
++ qCWarning(lcGenericStructHandler) << "QOpcUaMultiDimensionalArray value is expected to contain"
++ << "a generic struct, unable to encode";
++ return false;
++ }
++ }
++
++ auto success = encoder.encodeArray<quint32>(array.arrayDimensions());
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode array dimensions";
++ return false;
++ }
++
++ success = encoder.encode<qint32>(valueArray.size());
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode array length";
++ return false;
++ }
++
++ for (const auto &entry : valueArray) {
++ success = encodeStructInternal(encoder, entry.value<QOpcUaGenericStructValue>());
++
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to encode struct into the array";
++ return false;
++ }
++ }
++
++ return true;
++ }
++
++ return false;
++ }
++
++ // Maybe this is a supertype of a built-in type
++ const auto superType = m_knownSubtypes.constFind(dataTypeId);
++ if (superType != m_knownSubtypes.constEnd())
++ return encodeKnownTypesInternal(encoder, value, valueRank, superType.value());
++
++ return false;
++ }
++
++ void QOpcUaGenericStructHandlerPrivate::handleFinished(bool success)
++ {
++ if (m_hasError)
++ return;
++
++ m_finishedCount++;
++ m_hasError = !success;
++
++ Q_Q(QOpcUaGenericStructHandler);
++
++ if (m_finishedCount == 1 || m_hasError) {
++ m_initialized = !m_hasError;
++ emit q->initializedChanged(m_initialized);
++ }
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::addCustomStructureDefinition(const QOpcUaStructureDefinition &definition,
++ const QString &typeId, const QString &name, QOpcUa::IsAbstract isAbstract)
++ {
++ if (typeId.isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Failed to add custom structure definition, typeId must not be empty";
++ return false;
++ }
++
++ if (name.isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Failed to add custom structure definition, name must not be empty";
++ return false;
++ }
++
++ if (definition.defaultEncodingId().isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Failed to add custom structure definition, definition.defaultEncodingId() must not be empty";
++ return false;
++ }
++
++ StructMapEntry entry;
++ entry.isAbstract = isAbstract == QOpcUa::IsAbstract::Abstract;
++ entry.name = name;
++ entry.nodeId = typeId;
++ entry.structureDefinition = definition;
++
++ m_structuresByTypeId[typeId] = entry;
++ m_structuresByEncodingId[definition.defaultEncodingId()] = entry;
++ m_typeNamesByEncodingId[definition.defaultEncodingId()] = name;
++ m_typeNamesByTypeId[typeId] = name;
++
++ if (entry.isAbstract)
++ m_abstractTypeIds.insert(typeId);
++
++ return true;
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::addCustomEnumDefinition(const QOpcUaEnumDefinition &definition,
++ const QString &typeId, const QString &name,
++ QOpcUa::IsAbstract isAbstract)
++ {
++ if (typeId.isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Failed to add custom enum definition, typeId must not be empty";
++ return false;
++ }
++
++ if (name.isEmpty()) {
++ qCWarning(lcGenericStructHandler) << "Failed to add custom enum definition, name must not be empty";
++ return false;
++ }
++
++ EnumMapEntry entry;
++ entry.isAbstract = isAbstract == QOpcUa::IsAbstract::Abstract;
++ entry.name = name;
++ entry.nodeId = typeId;
++ entry.enumDefinition = definition;
++
++ m_enumsByTypeId[typeId] = entry;
++ m_typeNamesByTypeId[typeId] = name;
++
++ return true;
++ }
++
++ bool QOpcUaGenericStructHandlerPrivate::initialized() const
++ {
++ return m_initialized;
++ }
++
++ void QOpcUaGenericStructHandlerPrivate::handleInitializeFinished(bool success)
++ {
++ if (!success) {
++ qCWarning(lcGenericStructHandler) << "Failed to read the data type tree";
++ handleFinished(false);
++ return;
++ } else {
++ processDataTypeRecursive(m_baseDataType.get());
++ handleFinished(true);
++ }
++ }
++
++ void QOpcUaGenericStructHandlerPrivate::processDataTypeRecursive(QOpcUaInternalDataTypeNode *node)
++ {
++ qCDebug(lcGenericStructHandler) << "Found DataType:" << node->name();
++
++ m_typeNamesByTypeId[node->nodeId()] = node->name();
++ if (node->isAbstract())
++ m_abstractTypeIds.insert(node->nodeId());
++
++ for (const auto &child : node->children()) {
++ if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Structure))
++ processStructRecursive(child.get());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Enumeration))
++ processEnumRecursive(child.get());
++ else {
++ if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Boolean))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int32))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt32))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Double))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Float))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::String))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::LocalizedText))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::DateTime))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt16))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int16))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::UInt64))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Int64))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Byte))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::SByte))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::ByteString))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::XmlElement))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::NodeId))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::Guid))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::QualifiedName))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else if (child->nodeId() == QOpcUa::namespace0Id(QOpcUa::NodeIds::Namespace0::StatusCode))
++ processSubtypeOfKnownTypeRecursive(child.get(), child->nodeId());
++ else
++ processDataTypeRecursive(child.get());
++ }
++ }
++ }
++
++ void QOpcUaGenericStructHandlerPrivate::processStructRecursive(QOpcUaInternalDataTypeNode *node)
++ {
++ qCDebug(lcGenericStructHandler) << "Found struct:" << node->name();
++
++ m_typeNamesByTypeId.insert(node->nodeId(), node->name());
++
++ if (node->isAbstract())
++ m_abstractTypeIds.insert(node->nodeId());
++
++ const auto structureDefinition = node->definition().value<QOpcUaStructureDefinition>();
++ if (!structureDefinition.defaultEncodingId().isEmpty())
++ m_typeNamesByEncodingId.insert(structureDefinition.defaultEncodingId(), node->name());
++
++ const StructMapEntry entry{ node->name(), node->nodeId(), node->isAbstract(), structureDefinition };
++ m_structuresByEncodingId.insert(structureDefinition.defaultEncodingId(), entry);
++ m_structuresByTypeId.insert(node->nodeId(), entry);
++
++ for (const auto &child : node->children())
++ processStructRecursive(child.get());
++ }
++
++ void QOpcUaGenericStructHandlerPrivate::processEnumRecursive(QOpcUaInternalDataTypeNode *node)
++ {
++ qCDebug(lcGenericStructHandler) << "Found enum:" << node->name();
++
++ m_typeNamesByTypeId[node->nodeId()] = node->name();
++
++ if (node->isAbstract())
++ m_abstractTypeIds.insert(node->nodeId());
++
++ EnumMapEntry entry;
++ entry.name = node->name();
++ entry.enumDefinition = node->definition().value<QOpcUaEnumDefinition>();
++ entry.nodeId = node->nodeId();
++ entry.isAbstract = node->isAbstract();
++ m_enumsByTypeId.insert(node->nodeId(), entry);
++
++ for (const auto &child : node->children())
++ processEnumRecursive(child.get());
++ }
++
++ void QOpcUaGenericStructHandlerPrivate::processSubtypeOfKnownTypeRecursive(QOpcUaInternalDataTypeNode *node, const QString &id)
++ {
++ m_typeNamesByTypeId[node->nodeId()] = node->name();
++
++ if (node->isAbstract())
++ m_abstractTypeIds.insert(node->nodeId());
++
++ if (node->nodeId() != id)
++ m_knownSubtypes[node->nodeId()] = id;
++
++ for (const auto &child : node->children())
++ processSubtypeOfKnownTypeRecursive(child.get(), id);
++ }
++
++ QT_END_NAMESPACE