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

source code of qtopcua/src/opcua/client/qopcuagenericstructhandlerprivate.cpp