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

Provided by KDAB

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

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