| 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 <QtOpcUa/qopcuaenumdefinition.h> | 
| 5 | #include <QtOpcUa/qopcuagenericstructhandler.h> | 
| 6 | #include <QtOpcUa/qopcuaclient.h> | 
| 7 | #include <QtOpcUa/qopcuaextensionobject.h> | 
| 8 | #include <QtOpcUa/qopcuastructuredefinition.h> | 
| 9 | #include <QtOpcUa/qopcuabinarydataencoding.h> | 
| 10 | #include <QtOpcUa/qopcuamultidimensionalarray.h> | 
| 11 |  | 
| 12 | #include <private/qobject_p.h> | 
| 13 | #include <QLoggingCategory> | 
| 14 | #include <QPointer> | 
| 15 | #include <QSet> | 
| 16 |  | 
| 17 | // | 
| 18 | //  W A R N I N G | 
| 19 | //  ------------- | 
| 20 | // | 
| 21 | // This file is not part of the Qt API.  It exists purely as an | 
| 22 | // implementation detail.  This header file may change from version to | 
| 23 | // version without notice, or even be removed. | 
| 24 | // | 
| 25 | // We mean it. | 
| 26 | // | 
| 27 |  | 
| 28 | #ifndef QOPCUAGENERICSTRUCTHANDLERPRIVATE_H | 
| 29 | #define QOPCUAGENERICSTRUCTHANDLERPRIVATE_H | 
| 30 |  | 
| 31 | QT_BEGIN_NAMESPACE | 
| 32 |  | 
| 33 | Q_DECLARE_LOGGING_CATEGORY(lcGenericStructHandler); | 
| 34 |  | 
| 35 | class QOpcUaInternalDataTypeNode; | 
| 36 |  | 
| 37 | class QOpcUaGenericStructHandlerPrivate : public QObjectPrivate { | 
| 38 |     Q_DECLARE_PUBLIC(QOpcUaGenericStructHandler) | 
| 39 |  | 
| 40 |     Q_DISABLE_COPY(QOpcUaGenericStructHandlerPrivate) | 
| 41 | public: | 
| 42 |     QOpcUaGenericStructHandlerPrivate(QOpcUaClient *client); | 
| 43 |  | 
| 44 |     bool initialize(); | 
| 45 |  | 
| 46 |     QOpcUaGenericStructValue decode(const QOpcUaExtensionObject &extensionObject, bool &success) const; | 
| 47 |     bool encode(const QOpcUaGenericStructValue &value, QOpcUaExtensionObject &output); | 
| 48 |  | 
| 49 |     QOpcUaGenericStructValue createGenericStructValueForTypeId(const QString &typeId); | 
| 50 |  | 
| 51 |     QOpcUaStructureDefinition structureDefinitionForBinaryEncodingId(const QString &id) const; | 
| 52 |     QOpcUaStructureDefinition structureDefinitionForTypeId(const QString &id) const; | 
| 53 |     QOpcUaEnumDefinition enumDefinitionForTypeId(const QString &id) const; | 
| 54 |     QString typeNameForBinaryEncodingId(const QString &id) const; | 
| 55 |     QString typeNameForTypeId(const QString &id) const; | 
| 56 |     QOpcUaGenericStructHandler::DataTypeKind dataTypeKindForTypeId(const QString &id) const; | 
| 57 |     QString typeIdForBinaryEncodingId(const QString &id) const; | 
| 58 |     bool isAbstractTypeId(const QString &id) const; | 
| 59 |  | 
| 60 | Q_SIGNALS: | 
| 61 |     void initializeFinished(bool success); | 
| 62 |  | 
| 63 | protected: | 
| 64 |     QOpcUaGenericStructValue decodeStructInternal(QOpcUaBinaryDataEncoding &decoder, const QString &dataTypeId, | 
| 65 |                                                   bool &success, int currentDepth) const; | 
| 66 |     QVariant decodeKnownTypesInternal(QOpcUaBinaryDataEncoding &decoder, const QString &dataTypeId, qint32 valueRank, | 
| 67 |                                       bool &success, int currentDepth) const; | 
| 68 |     bool encodeStructInternal(QOpcUaBinaryDataEncoding &encoder, const QOpcUaGenericStructValue &value); | 
| 69 |     bool encodeKnownTypesInternal(QOpcUaBinaryDataEncoding &encoder, const QVariant &value, qint32 valueRank, const QString &dataTypeId); | 
| 70 |     void handleFinished(bool success); | 
| 71 |  | 
| 72 |     bool addCustomStructureDefinition(const QOpcUaStructureDefinition &definition, const QString &typeId, const QString &name, | 
| 73 |                                       QOpcUa::IsAbstract isAbstract); | 
| 74 |     bool addCustomEnumDefinition(const QOpcUaEnumDefinition &definition, const QString &typeId, const QString &name, | 
| 75 |                                  QOpcUa::IsAbstract isAbstract); | 
| 76 |  | 
| 77 |     bool initialized() const; | 
| 78 |  | 
| 79 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 80 |     QVariant decodeArrayOrScalar(QOpcUaBinaryDataEncoding &decoder, qint32 valueRank, bool &success) const | 
| 81 |     { | 
| 82 |         if (valueRank > 1) { | 
| 83 |             const auto arrayDimensions = decoder.decodeArray<quint32>(success); | 
| 84 |  | 
| 85 |             if (!success) | 
| 86 |                 return QVariant(); | 
| 87 |  | 
| 88 |             const auto data = decoder.decodeArray<T, OVERLAY>(success); | 
| 89 |  | 
| 90 |             if (!success) | 
| 91 |                 return QVariant(); | 
| 92 |  | 
| 93 |             QOpcUaMultiDimensionalArray matrix; | 
| 94 |             matrix.setArrayDimensions(arrayDimensions); | 
| 95 |             matrix.setValueArray({data.constBegin(), data.constEnd()}); | 
| 96 |             return matrix; | 
| 97 |         } | 
| 98 |  | 
| 99 |         return valueRank == 1 ? QVariant::fromValue(decoder.decodeArray<T, OVERLAY>(success)) : decoder.decode<T, OVERLAY>(success); | 
| 100 |     } | 
| 101 |  | 
| 102 |     template <typename T, QOpcUa::Types OVERLAY = QOpcUa::Types::Undefined> | 
| 103 |     bool encodeArrayOrScalar(QOpcUaBinaryDataEncoding &encoder, qint32 valueRank, const QVariant &value) | 
| 104 |     { | 
| 105 |         if ((valueRank == 1 && !value.canConvert<QList<T>>()) || (valueRank <= 0 && !value.canConvert<T>())) { | 
| 106 |             qCWarning(lcGenericStructHandler) << "Type mismatch for enum field, unable to encode" ; | 
| 107 |             return false; | 
| 108 |         } | 
| 109 |  | 
| 110 |         if (valueRank > 1) { | 
| 111 |             if (!value.canConvert<QOpcUaMultiDimensionalArray>()) { | 
| 112 |                 qCWarning(lcGenericStructHandler) << "Type mismatch for enum field, value rank > 1 requires QOpcUaMultiDimensionalArray" ; | 
| 113 |                 return false; | 
| 114 |             } | 
| 115 |  | 
| 116 |             auto array = value.value<QOpcUaMultiDimensionalArray>(); | 
| 117 |             QList<T> data; | 
| 118 |             for (const auto &entry : array.valueArray()) { | 
| 119 |                 if (!entry.canConvert<T>()) { | 
| 120 |                     qCWarning(lcGenericStructHandler) << "Invalid type in multi dimensional array" ; | 
| 121 |                 } | 
| 122 |                 data.push_back(entry.value<T>()); | 
| 123 |             } | 
| 124 |  | 
| 125 |             const auto success = encoder.encodeArray<quint32>(src: array.arrayDimensions()); | 
| 126 |  | 
| 127 |             if (!success) | 
| 128 |                 return false; | 
| 129 |  | 
| 130 |             return encoder.encodeArray<T, OVERLAY>(data); | 
| 131 |         } | 
| 132 |  | 
| 133 |         return valueRank == 1 ? encoder.encodeArray<T, OVERLAY>(value.value<QList<T>>()) : encoder.encode<T, OVERLAY>(value.value<T>()); | 
| 134 |     } | 
| 135 |  | 
| 136 | protected: | 
| 137 |     void handleInitializeFinished(bool success); | 
| 138 |     void processDataTypeRecursive(QOpcUaInternalDataTypeNode *node); | 
| 139 |     void processStructRecursive(QOpcUaInternalDataTypeNode *node); | 
| 140 |     void processEnumRecursive(QOpcUaInternalDataTypeNode *node); | 
| 141 |     void processSubtypeOfKnownTypeRecursive(QOpcUaInternalDataTypeNode *node, const QString &id); | 
| 142 |  | 
| 143 | private: | 
| 144 |     QPointer<QOpcUaClient> m_client; | 
| 145 |     QScopedPointer<QOpcUaInternalDataTypeNode> m_baseDataType; | 
| 146 |  | 
| 147 |     int m_finishedCount = 0; | 
| 148 |     bool m_hasError = false; | 
| 149 |  | 
| 150 |     class StructMapEntry { | 
| 151 |     public: | 
| 152 |         QString name; | 
| 153 |         QString nodeId; | 
| 154 |         bool isAbstract = false; | 
| 155 |         QOpcUaStructureDefinition structureDefinition; | 
| 156 |     }; | 
| 157 |  | 
| 158 |     class EnumMapEntry { | 
| 159 |     public: | 
| 160 |         QString name; | 
| 161 |         QString nodeId; | 
| 162 |         bool isAbstract = false; | 
| 163 |         QOpcUaEnumDefinition enumDefinition; | 
| 164 |     }; | 
| 165 |  | 
| 166 |     QHash<QString, StructMapEntry> m_structuresByEncodingId; | 
| 167 |     QHash<QString, StructMapEntry> m_structuresByTypeId; | 
| 168 |     QHash<QString, EnumMapEntry> m_enumsByTypeId; | 
| 169 |     QHash<QString, QString> m_typeNamesByTypeId; | 
| 170 |     QHash<QString, QString> m_typeNamesByEncodingId; | 
| 171 |     QSet<QString> m_abstractTypeIds; | 
| 172 |  | 
| 173 |     QHash<QString, QString> m_knownSubtypes; | 
| 174 |  | 
| 175 |     const int m_maxNestingLevel = 500; | 
| 176 |  | 
| 177 |     bool m_initialized = false; | 
| 178 | }; | 
| 179 |  | 
| 180 | QT_END_NAMESPACE | 
| 181 |  | 
| 182 | #endif // QOPCUAGENERICSTRUCTHANDLERPRIVATE_H | 
| 183 |  |