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 "util.h"
5#include "enumeratedtype.h"
6#include "field.h"
7#include "mappingfilegenerator.h"
8#include "stringidentifier.h"
9#include "structuredtype.h"
10
11#include <QtCore/qdir.h>
12#include <QtCore/qfile.h>
13#include <QtCore/qmap.h>
14#include <QtCore/qset.h>
15
16MappingFileGenerator::MappingFileGenerator(const QList<XmlElement *> &generateMapping,
17 const QString &path,
18 const QString &prefix,
19 const QString &header)
20 : m_generateMapping(generateMapping)
21 , m_path(path)
22 , m_prefix(prefix)
23 , m_header(header)
24{
25 for (const auto &type : m_generateMapping) {
26 const auto structuredType = dynamic_cast<StructuredType *>(type);
27 if (structuredType) {
28 for (const auto &possibleField : structuredType->fields()) {
29 for (const auto &field : structuredType->fields()) {
30 if (possibleField->name() == field->lengthField()
31 && !m_lengthFields.contains(t: possibleField))
32 m_lengthFields.push_back(t: possibleField);
33
34 if (!m_switchFields.contains(t: possibleField)
35 && possibleField->name() == field->switchField())
36 m_switchFields.push_back(t: possibleField);
37 }
38 }
39 }
40 }
41}
42
43void MappingFileGenerator::addGenerateMapping(
44 const QList<XmlElement *> &generateMapping)
45{
46 for (const auto &mapping : generateMapping) {
47 if (!m_generateMapping.contains(t: mapping))
48 m_generateMapping.push_back(t: mapping);
49 }
50 for (const auto &mapping : m_generateMapping) {
51 const auto structuredType = dynamic_cast<StructuredType *>(mapping);
52 if (structuredType) {
53 for (const auto &possibleField : structuredType->fields()) {
54 for (const auto &field : structuredType->fields()) {
55 if (possibleField->name() == field->lengthField()
56 && !m_lengthFields.contains(t: possibleField))
57 m_lengthFields.push_back(t: possibleField);
58
59 if (!m_switchFields.contains(t: possibleField)
60 && possibleField->name() == field->switchField())
61 m_switchFields.push_back(t: possibleField);
62 }
63 }
64 }
65 }
66}
67
68MappingFileGenerator::MappingError MappingFileGenerator::generateMapping()
69{
70 auto error = sortMappings();
71 if (error != NoError)
72 return error;
73
74 if (generateMappingHeader() == NoError)
75 error = generateMappingCpp();
76 else
77 error = UnanbleToWrite;
78 return error;
79}
80
81MappingFileGenerator::MappingError MappingFileGenerator::generateMappingHeader()
82{
83 QFile file;
84 const auto fileName = QStringLiteral("%1binarydeencoder%2")
85 .arg(args: m_prefix.toLower(), args: StringIdentifier::headerIdentifier);
86 QDir dir(m_path);
87 if (!dir.exists(name: m_path))
88 if (!dir.mkpath(dirPath: m_path))
89 return UnanbleToWrite;
90 file.setFileName(dir.absoluteFilePath(fileName));
91
92 file.open(flags: QIODevice::WriteOnly | QIODevice::Text);
93 QTextStream output(&file);
94
95 // Print header (if present)
96 if (!m_header.isEmpty())
97 output << m_header << Util::lineBreak(n: 2);
98 output << "#pragma once"
99 << Util::lineBreak(n: 2);
100 QList<QString> includes;
101 for (auto mapping : m_generateMapping) {
102 const auto enumeratedType = dynamic_cast<EnumeratedType *>(mapping);
103 if (enumeratedType) {
104 if (!includes.contains(
105 str: QLatin1String("#include \"%1enumerations.h\"%2").arg(args: m_prefix.toLower(), args: Util::lineBreak())))
106 includes.push_back(
107 t: QLatin1String("#include \"%1enumerations.h\"%2").arg(args: m_prefix.toLower(), args: Util::lineBreak()));
108 } else {
109 const auto structuredType = dynamic_cast<StructuredType *>(mapping);
110 if (structuredType)
111 if (!includes.contains(
112 str: QLatin1String("#include \"%1%2.h\"%3")
113 .arg(args: m_prefix.toLower(), args: structuredType->name().toLower(), args: Util::lineBreak())))
114 includes.push_back(
115 t: QLatin1String("#include \"%1%2.h\"%3")
116 .arg(args: m_prefix.toLower(), args: structuredType->name().toLower(), args: Util::lineBreak()));
117 }
118 }
119
120 includes.sort();
121 for (const auto &include : includes) {
122 output << include;
123 }
124 output << Util::lineBreak();
125 output << "#include <QtOpcUa/QOpcUaBinaryDataEncoding>"
126 << Util::lineBreak();
127 output << Util::lineBreak();
128
129 output << "class " << m_prefix << "BinaryDeEncoder"
130 << Util::lineBreak();
131 output << "{"
132 << Util::lineBreak();
133 output << "public:"
134 << Util::lineBreak();
135 output << Util::lineBreak();
136 output << Util::indent(level: 1) << m_prefix << "BinaryDeEncoder(QByteArray *buffer);"
137 << Util::lineBreak();
138 output << Util::indent(level: 1) << m_prefix << "BinaryDeEncoder(QOpcUaExtensionObject &object);"
139 << Util::lineBreak();
140 output << Util::lineBreak();
141 output << Util::indent(level: 1) << "template <typename T>"
142 << Util::lineBreak();
143 output << Util::indent(level: 1) << "T decode(bool &success);"
144 << Util::lineBreak();
145 output << Util::lineBreak();
146 output << Util::indent(level: 1) << "template <typename T>"
147 << Util::lineBreak();
148 output << Util::indent(level: 1) << "QList<T> decodeArray(bool &success);"
149 << Util::lineBreak();
150 output << Util::lineBreak();
151 output << Util::indent(level: 1) << "template <typename T>"
152 << Util::lineBreak();
153 output << Util::indent(level: 1) << "bool encode(const T &src);"
154 << Util::lineBreak();
155 output << Util::lineBreak();
156 output << Util::indent(level: 1) << "template <typename T>"
157 << Util::lineBreak();
158 output << Util::indent(level: 1) << "bool encodeArray(const QList<T> &src);"
159 << Util::lineBreak();
160 output << Util::lineBreak();
161 output << Util::indent(level: 1) << "QOpcUaBinaryDataEncoding &binaryDataEncoding();"
162 << Util::lineBreak();
163 output << Util::lineBreak();
164
165 output << "private:"
166 << Util::lineBreak();
167 output << Util::indent(level: 1) << "QScopedPointer<QOpcUaBinaryDataEncoding> m_binaryDataEncoding;"
168 << Util::lineBreak();
169
170 output << "};"
171 << "\n\n";
172 generateDeEncodingArray(output);
173 generateDeEncoding(output);
174 return NoError;
175}
176
177MappingFileGenerator::MappingError MappingFileGenerator::generateMappingCpp()
178{
179 QFile file;
180 QDir dir(m_path);
181 const auto fileName = QStringLiteral("%1binarydeencoder%2")
182 .arg(args: m_prefix.toLower(), args: StringIdentifier::cppIdentifier);
183 if (!dir.exists(name: m_path))
184 if (!dir.mkpath(dirPath: m_path))
185 return UnanbleToWrite;
186 file.setFileName(dir.absoluteFilePath(fileName));
187
188 file.open(flags: QIODevice::WriteOnly | QIODevice::Text);
189 QTextStream output(&file);
190
191 // Print header (if present)
192 if (!m_header.isEmpty())
193 output << m_header << "\n\n";
194
195 output << "#include \"" << m_prefix.toLower() << "binarydeencoder.h\"" << Util::lineBreak();
196 output << Util::lineBreak();
197 output << m_prefix << "BinaryDeEncoder::" << m_prefix << "BinaryDeEncoder (QByteArray *buffer)"
198 << Util::lineBreak();
199 output << Util::indent(level: 1) << ": m_binaryDataEncoding(new QOpcUaBinaryDataEncoding(buffer))"
200 << Util::lineBreak();
201 output << "{"
202 << Util::lineBreak();
203 output << "}"
204 << Util::lineBreak(n: 2);
205 output << m_prefix << "BinaryDeEncoder::" << m_prefix
206 << "BinaryDeEncoder "
207 "(QOpcUaExtensionObject &object) "
208 << Util::lineBreak();
209 output << Util::indent(level: 1) << ": m_binaryDataEncoding(new "
210 "QOpcUaBinaryDataEncoding(&object.encodedBodyRef()))"
211 << Util::lineBreak();
212 output << "{"
213 << Util::lineBreak();
214 output << "}"
215 << Util::lineBreak();
216 output << "QOpcUaBinaryDataEncoding &" << m_prefix << "BinaryDeEncoder::binaryDataEncoding()"
217 << Util::lineBreak();
218 output << "{"
219 << Util::lineBreak();
220 output << Util::indent(level: 1) << "return *m_binaryDataEncoding.data();"
221 << Util::lineBreak();
222 output << "}"
223 << Util::lineBreak();
224 return NoError;
225}
226
227void MappingFileGenerator::generateFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
228{
229 output << Util::indent(level) << "temp.set" << field->name() << "(";
230 generateScalarOrArrayDecoding(output, structuredType, field);
231 output << ");" << Util::lineBreak();
232 output << Util::indent(level) << "if (!success)" << Util::lineBreak();
233 output << Util::indent(level: level + 1) << "return {};" << Util::lineBreak();
234}
235
236void MappingFileGenerator::generateScalarOrArrayDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field)
237{
238 Q_UNUSED(structuredType);
239 const auto builtinType = StringIdentifier::typeNameDataTypeConverter.constFind(key: field->typeName());
240 if (builtinType != StringIdentifier::typeNameDataTypeConverter.constEnd()) {
241 output << "m_binaryDataEncoding->decode" << (field->lengthField().isEmpty() ? "" : "Array") << "<" << builtinType.value() << ">(success)";
242 return;
243 }
244
245 bool isPrecoded = false;
246 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
247 if (precodedType.contains(name: Util::removeNamespace(typeName: field->typeName()))) {
248 output << "m_binaryDataEncoding->decode";
249 if (!field->lengthField().isEmpty())
250 output << "Array";
251 output << "<";
252 if (!precodedType.deEncoderName().isEmpty())
253 output << precodedType.deEncoderName() << ", QOpcUa::Types::NodeId";
254 else
255 output << precodedType.className();
256 output << ">(success)";
257 isPrecoded = true;
258 break;
259 }
260 }
261 if (!isPrecoded) {
262 output << "decode";
263 if (!field->lengthField().isEmpty())
264 output << "Array";
265 output << "<" << m_prefix << (field->isEnum() ? "::" : "") << Util::removeNamespace(typeName: field->typeName()) << ">(success)";
266 }
267}
268
269void MappingFileGenerator::generateOptionalFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
270{
271 int index = -1;
272 QSet<QString> usedSwitchFields;
273 for (const auto &current : structuredType->fields()) {
274 if (!current->switchField().isEmpty() && !usedSwitchFields.contains(value: current->switchField())) {
275 index++;
276 usedSwitchFields.insert(value: current->switchField());
277 }
278
279 if (current == field)
280 break;
281 }
282
283 output << Util::indent(level) << "if (decodingMask & " << QStringLiteral("(1 << %1)").arg(a: index) << ") {" << Util::lineBreak();
284 generateFieldDecoding(output, structuredType, field, level: level + 1);
285 output << Util::indent(level: level + 1) << "temp.set" << field->switchField() << "(true);" << Util::lineBreak();
286 output << Util::indent(level) << "}" << Util::lineBreak();
287}
288
289void MappingFileGenerator::generateUnionFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
290{
291 output << Util::indent(level) << QStringLiteral("if (switchField == %1) {").arg(a: field->switchValue()) << Util::lineBreak();
292 generateFieldDecoding(output, structuredType, field, level: level + 1);
293 output << Util::indent(level) << "}" << Util::lineBreak();
294}
295
296void MappingFileGenerator::generateFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
297{
298 output << Util::indent(level) << "if (!";
299 generateScalarOrArrayEncoding(output, structuredType, field);
300 output << ")" << Util::lineBreak();
301 output << Util::indent(level: level + 1) << "return false;" << Util::lineBreak();
302}
303
304void MappingFileGenerator::generateScalarOrArrayEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field)
305{
306 Q_UNUSED(structuredType);
307 const auto builtinType = StringIdentifier::typeNameDataTypeConverter.constFind(key: field->typeName());
308 if (builtinType != StringIdentifier::typeNameDataTypeConverter.constEnd()) {
309 output << "m_binaryDataEncoding->encode" << (field->lengthField().isEmpty() ? "" : "Array") << "<" << builtinType.value() << ">(src." << field->lowerFirstName() << "())";
310 return;
311 }
312
313 bool isPrecoded = false;
314 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
315 if (precodedType.contains(name: Util::removeNamespace(typeName: field->typeName()))) {
316 output << "m_binaryDataEncoding->encode";
317 if (!field->lengthField().isEmpty())
318 output << "Array";
319 output << "<";
320 if (!precodedType.deEncoderName().isEmpty())
321 output << precodedType.deEncoderName() << ", QOpcUa::Types::NodeId";
322 else
323 output << precodedType.className();
324 output << ">(src." << field->lowerFirstName() << "())";
325 isPrecoded = true;
326 break;
327 }
328 }
329 if (!isPrecoded) {
330 output << "encode";
331 if (!field->lengthField().isEmpty())
332 output << "Array";
333 output << "<" << m_prefix << (field->isEnum() ? "::" : "") << Util::removeNamespace(typeName: field->typeName()) << ">(src." << field->lowerFirstName() << "())";
334 }
335}
336
337void MappingFileGenerator::generateOptionalFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
338{
339 output << Util::indent(level) << "if (src." << Util::lowerFirstLetter(temp: field->switchField()) << "()) {" << Util::lineBreak();
340 generateFieldEncoding(output, structuredType, field, level: level + 1);
341 output << Util::indent(level) << "}" << Util::lineBreak();
342}
343
344void MappingFileGenerator::generateUnionFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
345{
346 output << Util::indent(level) << "if (static_cast<quint32>(src." << structuredType->fields().constFirst()->lowerFirstName() << "()) == " << field->switchValue() << ") {" << Util::lineBreak();
347 generateFieldEncoding(output, structuredType, field, level: level + 1);
348 output << Util::indent(level) << "}" << Util::lineBreak();
349}
350
351void MappingFileGenerator::generateDeEncodingArray(QTextStream &output)
352{
353 output << "template<typename T>"
354 << Util::lineBreak();
355 output << "inline QList<T> " << m_prefix << "BinaryDeEncoder::decodeArray(bool &success)"
356 << Util::lineBreak();
357 output << "{"
358 << Util::lineBreak();
359 output << Util::indent(level: 1) << "QList<T> temp;"
360 << Util::lineBreak(n: 2);
361 output << Util::indent(level: 1) << "qint32 size = m_binaryDataEncoding->decode<qint32>(success);"
362 << Util::lineBreak();
363 output << Util::indent(level: 1) << "if (!success)"
364 << Util::lineBreak();
365 output << Util::indent(level: 2) << "return {};";
366 output << Util::lineBreak(n: 2);
367 output << Util::indent(level: 1) << "for (int i = 0; i < size; ++i) {"
368 << Util::lineBreak();
369 output << Util::indent(level: 2) << "temp.push_back(decode<T>(success));"
370 << Util::lineBreak();
371 output << Util::indent(level: 2) << "if (!success)"
372 << Util::lineBreak();
373 output << Util::indent(level: 3) << "return {};"
374 << Util::lineBreak();
375 output << Util::indent(level: 1) << "}"
376 << Util::lineBreak(n: 2);
377 output << Util::indent(level: 1) << "return temp;"
378 << Util::lineBreak();
379 output << "}"
380 << Util::lineBreak(n: 2);
381 output << "template<typename T>"
382 << Util::lineBreak();
383 output << "inline bool " << m_prefix << "BinaryDeEncoder::encodeArray(const QList<T> &src)"
384 << Util::lineBreak();
385 output << "{"
386 << Util::lineBreak();
387 output << Util::indent(level: 1) << "if (src.size() > (std::numeric_limits<qint32>::max()))"
388 << Util::lineBreak();
389 output << Util::indent(level: 2) << "return false;"
390 << Util::lineBreak(n: 2);
391 output << Util::indent(level: 1) << "if (!m_binaryDataEncoding->encode<qint32>(src.size()))"
392 << Util::lineBreak();
393 output << Util::indent(level: 2) << "return false;"
394 << Util::lineBreak();
395 output << Util::indent(level: 1) << "for (const auto &element : src) {"
396 << Util::lineBreak();
397 output << Util::indent(level: 2) << "if (!encode<T>(element))"
398 << Util::lineBreak();
399 output << Util::indent(level: 3) << "return false;"
400 << Util::lineBreak();
401 output << Util::indent(level: 1) << "}"
402 << Util::lineBreak();
403 output << Util::indent(level: 1) << "return true;"
404 << Util::lineBreak();
405 output << "}"
406 << Util::lineBreak(n: 2);
407}
408
409void MappingFileGenerator::generateDeEncoding(QTextStream &output)
410{
411 for (const auto &mapping : m_generateMapping) {
412 const auto enumeratedType = dynamic_cast<EnumeratedType *>(mapping);
413 if (enumeratedType) {
414 generateDecodingEnumeratedType(output, enumeratedType);
415 generateEncodingEnumeratedType(output, enumeratedType);
416 } else {
417 const auto structuredType = dynamic_cast<StructuredType *>(mapping);
418 if (structuredType) {
419 generateDecodingStructuredType(output, structuredType);
420 generateEncodingStructuredType(output, structuredType);
421 }
422 }
423 }
424}
425
426void MappingFileGenerator::generateDecodingEnumeratedType(QTextStream &output,
427 const EnumeratedType *enumeratedType)
428{
429 output << "template<>"
430 << Util::lineBreak();
431 output << "inline " << m_prefix << "::" << enumeratedType->name() << " " << m_prefix
432 << "BinaryDeEncoder::decode(bool &success)"
433 << Util::lineBreak();
434 output << "{"
435 << Util::lineBreak();
436 output << Util::indent(level: 1) << "return static_cast<" << m_prefix << "::" << enumeratedType->name() << ">("
437 "m_binaryDataEncoding->decode<qint32>(success));"
438 << Util::lineBreak();
439 output << "}"
440 << Util::lineBreak(n: 2);
441}
442
443void MappingFileGenerator::generateEncodingEnumeratedType(QTextStream &output,
444 const EnumeratedType *enumeratedType)
445{
446 output << "template<>"
447 << Util::lineBreak();
448 output << "inline bool " << m_prefix << "BinaryDeEncoder"
449 << "::encode<" << m_prefix << "::" << enumeratedType->name() << ">(const " << m_prefix
450 << "::" << enumeratedType->name() << " &src)"
451 << Util::lineBreak();
452 output << "{"
453 << Util::lineBreak();
454 output << Util::indent(level: 1) << "return m_binaryDataEncoding->encode<qint32>(static_cast<qint32>(src));"
455 << Util::lineBreak();
456 output << "}"
457 << Util::lineBreak(n: 2);
458}
459
460void MappingFileGenerator::generateDecodingStructuredType(QTextStream &output,
461 const StructuredType *structuredType)
462{
463 output << "template<>"
464 << Util::lineBreak();
465 output << "inline " << m_prefix << structuredType->name() << " " << m_prefix
466 << "BinaryDeEncoder::decode<" << m_prefix << structuredType->name() << ">(bool &success)" << Util::lineBreak()
467 << "{" << Util::lineBreak();
468 output << Util::indent(level: 1) << m_prefix << structuredType->name() << " temp;"
469 << Util::lineBreak(n: 2);
470
471 if (structuredType->containsBitMask()) {
472 output << Util::indent(level: 1) <<"const auto decodingMask = "
473 << "m_binaryDataEncoding->decode<quint32>(success);" << Util::lineBreak()
474 << Util::indent(level: 1) << "if (!success)" << Util::lineBreak()
475 << Util::indent(level: 2) << "return {};" << Util::lineBreak(n: 2);
476 } else if (structuredType->hasUnion()) {
477 output << Util::indent(level: 1) <<"const auto switchField = "
478 << "m_binaryDataEncoding->decode<quint32>(success);" << Util::lineBreak()
479 << Util::indent(level: 1) << "if (!success)" << Util::lineBreak()
480 << Util::indent(level: 2) << "return {};" << Util::lineBreak(n: 2);
481 }
482
483 for (const auto &field : structuredType->fields()) {
484 if (field->isInStructuredTypeBitMask())
485 continue;
486
487 if (m_lengthFields.contains(t: field))
488 continue;
489
490 // The switch field is not a member of the generated data class
491 if (structuredType->hasUnion() && field == structuredType->fields().constFirst())
492 continue;
493
494 if (!field->switchField().isEmpty() && !field->isUnion())
495 generateOptionalFieldDecoding(output, structuredType, field);
496 else if (structuredType->hasUnion())
497 generateUnionFieldDecoding(output, structuredType, field);
498 else
499 generateFieldDecoding(output, structuredType, field);
500
501 output << Util::lineBreak();
502 }
503
504 output << Util::indent(level: 1) << "return temp;" << Util::lineBreak();
505 output << "}" << Util::lineBreak(n: 2);
506}
507
508void MappingFileGenerator::generateEncodingStructuredType(QTextStream &output,
509 const StructuredType *structuredType)
510{
511 output << "template<>"
512 << Util::lineBreak();
513 output << "inline bool " << m_prefix << "BinaryDeEncoder"
514 << "::encode<" << m_prefix << structuredType->name() << ">(const " << m_prefix
515 << structuredType->name() << " &src)"
516 << Util::lineBreak();
517 output << "{"
518 << Util::lineBreak();
519
520 // Create encoding mask for bitmask
521 if (structuredType->hasSwitchfield() && !structuredType->hasUnion()) {
522 output << Util::indent(level: 1) << "quint32 encodingMask = 0;" << Util::lineBreak();
523 int index = 0;
524 for (const auto &field : structuredType->fields()) {
525 if (!m_switchFields.contains(t: field))
526 continue;
527 output << Util::indent(level: 1) << "encodingMask |= ((src." << field->lowerFirstName() << "() ? 1 : 0) << " << index++ << ");" << Util::lineBreak();
528 }
529 output << Util::lineBreak();
530 output << Util::indent(level: 1) << "if (!m_binaryDataEncoding->encode<quint32>(encodingMask))" << Util::lineBreak();
531 output << Util::indent(level: 2) << "return false;" << Util::lineBreak(n: 2);
532 } else if (structuredType->hasUnion()) {
533 output << Util::indent(level: 1) << "if (!m_binaryDataEncoding->encode<quint32>(static_cast<quint32>(src." << structuredType->fields().constFirst()->lowerFirstName() << "())))" << Util::lineBreak();
534 output << Util::indent(level: 2) << "return false;" << Util::lineBreak(n: 2);
535 }
536
537 for (const auto &field : structuredType->fields()) {
538 if (m_lengthFields.contains(t: field))
539 continue;
540
541 if (m_switchFields.contains(t: field))
542 continue;
543
544 // Ignore reserved fields
545 if (field->isInStructuredTypeBitMask())
546 continue;
547
548 if (structuredType->hasUnion())
549 generateUnionFieldEncoding(output, structuredType, field);
550 else if (!field->switchField().isEmpty() && !field->isUnion())
551 generateOptionalFieldEncoding(output, structuredType, field);
552 else
553 generateFieldEncoding(output, structuredType, field);
554
555 output << Util::lineBreak();
556 }
557
558 output << Util::indent(level: 1) << "return true;" << Util::lineBreak();
559 output << "}" << Util::lineBreak(n: 2);
560}
561
562MappingFileGenerator::MappingError MappingFileGenerator::sortMappings()
563{
564 QList<XmlElement *> baseType;
565 QList<XmlElement *> extendedType;
566 for (const auto &mapping : m_generateMapping) {
567 const auto structuredType = dynamic_cast<StructuredType *>(mapping);
568 if (structuredType) {
569 bool isBaseType = true;
570 for (const auto &field : structuredType->fields()) {
571 bool isPreCoded = false;
572 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
573 if (precodedType.contains(name: Util::removeNamespace(typeName: field->typeName()))) {
574 isPreCoded = true;
575 break;
576 }
577 }
578 if (!isPreCoded)
579 if (!StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName()))
580 if (Util::removeNamespace(typeName: field->typeName()) != structuredType->name())
581 isBaseType = false;
582 }
583 if (isBaseType)
584 baseType.push_back(t: structuredType);
585 else
586 extendedType.push_back(t: structuredType);
587 } else {
588 baseType.push_front(t: mapping);
589 }
590 }
591 m_generateMapping = baseType;
592
593 while (!extendedType.isEmpty()) {
594 QList<XmlElement *> tmpExtendedType = extendedType;
595 for (int i = 0; i < extendedType.size(); i++) {
596 const auto structuredType = dynamic_cast<StructuredType *>(extendedType.at(i));
597 if (structuredType) {
598 bool independentExtended = true;
599 for (const auto &field : structuredType->fields()) {
600 bool isPreCoded = false;
601 for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
602 if (precodedType.contains(name: Util::removeNamespace(typeName: field->typeName()))) {
603 isPreCoded = true;
604 break;
605 }
606 }
607 bool basicdependecy = false;
608 if (StringIdentifier::typeNameDataTypeConverter.contains(key: field->typeName()))
609 basicdependecy = true;
610 else if (!isPreCoded
611 && Util::removeNamespace(typeName: field->typeName()) != structuredType->name()) {
612 for (const auto &type : m_generateMapping) {
613 if (type->name() == Util::removeNamespace(typeName: field->typeName())) {
614 basicdependecy = true;
615 }
616 }
617 } else if (!isPreCoded
618 && Util::removeNamespace(typeName: field->typeName()) == structuredType->name()) {
619 basicdependecy = true;
620 } else if (isPreCoded)
621 basicdependecy = true;
622 independentExtended &= basicdependecy;
623 }
624
625 if (independentExtended) {
626 m_generateMapping.append(t: structuredType);
627 extendedType.removeAt(i: extendedType.indexOf(t: structuredType));
628 }
629 }
630 }
631 if (extendedType == tmpExtendedType) {
632 return MappingFileGenerator::MappingError::UnableToResolve;
633 }
634 }
635 return MappingFileGenerator::MappingError::NoError;
636}
637

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtopcua/tools/datatypecodegenerator/mappingfilegenerator.cpp