summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorMartin Klos <martin.klos@basyskom.com>2021-02-23 11:27:16 +0100
committerJannis Voelker <jannis.voelker@basyskom.com>2023-09-28 10:08:35 +0200
commitc45f543f182a8b4139dd03df3c388f060d5097ff (patch)
tree11b462b2fb371afc422dbefe9740fed083fd5f69 /tools
parentbd2ef3a13704b48bd712c43d7063327d14c287dd (diff)
Add data type code generator
This change adds a code generator which generates data classes, enums and encoder/decoder methods for OPC UA data type descriptions in a .bsd file. The generated data classes provide a getter and setter based API that follows the structure of the types available in Qt OPC UA. If a structure field's type is already available in Qt OPC UA, it will be used in the data class instead of generating a new data class for it. The decoder/encoder operates on QOpcUaExtensionObject as well as QByteArray and can be used to turn received binary encoded values into easily accessible structured data as well as to create binary encoded extension objects for writing custom types or for parameters in method calls. Change-Id: I8090dcb9b5f2d26d9ad02e3d6d5c45b3baa5c777 Reviewed-by: Frank Meerkoetter <frank.meerkoetter@basyskom.com>
Diffstat (limited to 'tools')
-rw-r--r--tools/CMakeLists.txt4
-rw-r--r--tools/datatypecodegenerator/CMakeLists.txt29
-rw-r--r--tools/datatypecodegenerator/datatypefilewriter.cpp1287
-rw-r--r--tools/datatypecodegenerator/datatypefilewriter.h80
-rw-r--r--tools/datatypecodegenerator/dependencydatatypevalidator.cpp107
-rw-r--r--tools/datatypecodegenerator/dependencydatatypevalidator.h38
-rw-r--r--tools/datatypecodegenerator/enumeratedtype.cpp58
-rw-r--r--tools/datatypecodegenerator/enumeratedtype.h34
-rw-r--r--tools/datatypecodegenerator/enumeratedvalue.cpp33
-rw-r--r--tools/datatypecodegenerator/enumeratedvalue.h24
-rw-r--r--tools/datatypecodegenerator/field.cpp142
-rw-r--r--tools/datatypecodegenerator/field.h62
-rw-r--r--tools/datatypecodegenerator/import.cpp42
-rw-r--r--tools/datatypecodegenerator/import.h28
-rw-r--r--tools/datatypecodegenerator/main.cpp181
-rw-r--r--tools/datatypecodegenerator/mappingfilegenerator.cpp636
-rw-r--r--tools/datatypecodegenerator/mappingfilegenerator.h64
-rw-r--r--tools/datatypecodegenerator/recursivedescentparser.cpp389
-rw-r--r--tools/datatypecodegenerator/recursivedescentparser.h66
-rw-r--r--tools/datatypecodegenerator/stringidentifier.cpp127
-rw-r--r--tools/datatypecodegenerator/stringidentifier.h101
-rw-r--r--tools/datatypecodegenerator/structuredtype.cpp118
-rw-r--r--tools/datatypecodegenerator/structuredtype.h56
-rw-r--r--tools/datatypecodegenerator/typedictionary.cpp116
-rw-r--r--tools/datatypecodegenerator/typedictionary.h46
-rw-r--r--tools/datatypecodegenerator/util.cpp31
-rw-r--r--tools/datatypecodegenerator/util.h15
-rw-r--r--tools/datatypecodegenerator/visitor.h26
-rw-r--r--tools/datatypecodegenerator/xmlelement.cpp32
-rw-r--r--tools/datatypecodegenerator/xmlelement.h26
30 files changed, 3998 insertions, 0 deletions
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
index fdd27f0..58cf019 100644
--- a/tools/CMakeLists.txt
+++ b/tools/CMakeLists.txt
@@ -1,6 +1,10 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
+if(QT_FEATURE_datatypecodegenerator)
+ add_subdirectory(datatypecodegenerator)
+endif()
+
if(QT_FEATURE_ns0idgenerator)
add_subdirectory(defaultnodeidsgenerator)
endif()
diff --git a/tools/datatypecodegenerator/CMakeLists.txt b/tools/datatypecodegenerator/CMakeLists.txt
new file mode 100644
index 0000000..98bc5c3
--- /dev/null
+++ b/tools/datatypecodegenerator/CMakeLists.txt
@@ -0,0 +1,29 @@
+# Copyright (C) 2023 basysKom GmbH
+# SPDX-License-Identifier: BSD-3-Clause
+
+#####################################################################
+## qopcuaxmldatatypes2cpp Tool:
+#####################################################################
+
+qt_get_tool_target_name(target_name qopcuaxmldatatypes2cpp)
+qt_internal_add_tool(${target_name}
+ TOOLS_TARGET
+ QtOpcUa
+ SOURCES
+ main.cpp
+ util.cpp util.h
+ datatypefilewriter.cpp datatypefilewriter.h
+ dependencydatatypevalidator.cpp dependencydatatypevalidator.h
+ enumeratedtype.cpp enumeratedtype.h
+ enumeratedvalue.cpp enumeratedvalue.h
+ field.cpp field.h
+ import.cpp import.h
+ mappingfilegenerator.cpp mappingfilegenerator.h
+ recursivedescentparser.cpp recursivedescentparser.h
+ stringidentifier.cpp stringidentifier.h
+ structuredtype.cpp structuredtype.h
+ typedictionary.cpp typedictionary.h
+ visitor.h
+ xmlelement.cpp xmlelement.h
+)
+qt_internal_return_unless_building_tools()
diff --git a/tools/datatypecodegenerator/datatypefilewriter.cpp b/tools/datatypecodegenerator/datatypefilewriter.cpp
new file mode 100644
index 0000000..aaa3139
--- /dev/null
+++ b/tools/datatypecodegenerator/datatypefilewriter.cpp
@@ -0,0 +1,1287 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+#include "datatypefilewriter.h"
+#include "enumeratedtype.h"
+#include "enumeratedvalue.h"
+#include "field.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qset.h>
+
+DataTypeFileWriter::DataTypeFileWriter(const QString &path,
+ const QString &prefix,
+ const QString &header)
+ : m_path(path)
+ , m_prefix(prefix)
+ , m_header(header)
+{}
+
+void DataTypeFileWriter::visit(XmlElement *xmlElement)
+{
+ Q_UNUSED(xmlElement);
+}
+
+void DataTypeFileWriter::visit(EnumeratedType *enumeratedType)
+{
+ bool isPrecodedType = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(enumeratedType->name())) {
+ isPrecodedType = true;
+ break;
+ }
+ }
+ if (!isPrecodedType) {
+ m_generateMapping.append(enumeratedType);
+ }
+}
+
+void DataTypeFileWriter::visit(EnumeratedValue *enumeratedValue)
+{
+ Q_UNUSED(enumeratedValue);
+}
+
+void DataTypeFileWriter::visit(Field *field)
+{
+ Q_UNUSED(field);
+}
+
+void DataTypeFileWriter::visit(Import *import)
+{
+ Q_UNUSED(import);
+}
+
+void DataTypeFileWriter::visit(StructuredType *structuredType)
+{
+ if (!structuredType->fields().empty()) {
+ bool isPrecodedType = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(structuredType->name())) {
+ isPrecodedType = true;
+ break;
+ }
+ }
+ if (!isPrecodedType) {
+ m_generateMapping.append(structuredType);
+ }
+ }
+}
+
+void DataTypeFileWriter::visit(TypeDictionary *typeDictionary)
+{
+ Q_UNUSED(typeDictionary);
+}
+
+void DataTypeFileWriter::writeLicenseHeader(QTextStream &output)
+{
+ if (!m_header.isEmpty())
+ output << m_header << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCpp(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ if (!m_generatedStructuredTypeFilenames.contains(
+ QStringLiteral("%1%2").arg(m_prefix, structuredType->name())))
+ m_generatedStructuredTypeFilenames.push_back(
+ QStringLiteral("%1%2").arg(m_prefix, structuredType->name()));
+ output << "#include \"" << m_prefix.toLower() << structuredType->name().toLower() << ".h\"";
+
+ QList<QString> mutualIncludes;
+ for (const auto field : structuredType->fields()) {
+ const auto typeName = field->typeNameSecondPart();
+ for (const auto &type : m_generateMapping) {
+ StructuredType *tempStructuredType = dynamic_cast<StructuredType *>(type);
+ if (tempStructuredType) {
+ if (tempStructuredType == structuredType)
+ continue;
+ for (const auto &tempField : tempStructuredType->fields()) {
+ if (typeName == tempStructuredType->name()
+ && tempField->typeNameSecondPart() == structuredType->name()) {
+ if (!mutualIncludes.contains(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), tempStructuredType->name().toLower())))
+ mutualIncludes.push_back(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), tempStructuredType->name().toLower()));
+ }
+ }
+ }
+ }
+ }
+ if (!mutualIncludes.isEmpty()) {
+ output << Util::lineBreak();
+ std::sort(mutualIncludes.begin(), mutualIncludes.end());
+ for (const auto &include : mutualIncludes) {
+ output << include;
+ }
+ }
+ if (structuredType->recursive()) {
+ output << Util::lineBreak();
+ output << "#include <optional>";
+ output << Util::lineBreak();
+ }
+ output << Util::lineBreak(2);
+
+ writeStructuredTypeCppClassComment(structuredType, output);
+ writeStructuredTypeCppDataClass(structuredType, output);
+ writeStructuredTypeCppConstructors(structuredType, output);
+ writeStructuredTypeCppOperatorAssignment(structuredType, output);
+ writeStructuredTypeCppOperatorEquality(structuredType, output);
+ writeStructuredTypeCppQVariant(structuredType, output);
+ writeStructuredTypeCppDestructor(structuredType, output);
+ writeStructuredTypeCppGetterSetter(structuredType, output);
+ writeStructuredTypeCppDebug(structuredType, output);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppClassComment(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << Util::indent(1) << "\\class " << m_prefix << structuredType->name() << Util::lineBreak();
+ output << Util::indent(1) << "\\brief the OPC UA " << structuredType->name() << "."
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDataClass(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "class " << m_prefix << structuredType->name() << "Data : public QSharedData"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "public:"
+ << Util::lineBreak();
+
+ writeStructuredTypeCppDataClassMember(structuredType, output);
+
+ output << "};"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDataClassMember(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<Field *> unionMember;
+ if (structuredType->hasUnion()) {
+ for (const auto &possibleUnionMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (field->isUnion()) {
+ if (field->switchField() == possibleUnionMember->name()
+ && !unionMember.contains(possibleUnionMember))
+ unionMember.push_back(possibleUnionMember);
+ }
+ }
+ }
+ }
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleArrayLengthField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleArrayLengthField->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleArrayLengthField))
+ arrayLengthfields.push_back(possibleArrayLengthField);
+ }
+ }
+
+ QList<Field *> switchFields;
+ if (structuredType->hasSwitchfield()) {
+ for (const auto &possibleOptionalMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!possibleOptionalMember->switchField().isEmpty() && possibleOptionalMember->switchField() == field->name())
+ switchFields.push_back(field);
+ }
+ }
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (field->isInStructuredTypeBitMask() && !switchFields.contains(field))
+ continue;
+
+ bool isEnumeration = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1)) {
+ isEnumeration = true;
+ field->setIsEnum(true);
+ }
+ }
+
+ bool isPreCoded = false;
+ if (!arrayLengthfields.contains(field)) {
+ if (!unionMember.contains(field)) {
+ output << Util::indent(1);
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (!field->needContainer()) {
+ output << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " ";
+ } else {
+ output << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> ";
+ }
+ } else {
+ const auto typeName = field->typeNameSecondPart();
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ if (field->needContainer())
+ output << "QList<";
+ if (preCodedType.deEncoderName().isEmpty())
+ output << preCodedType.className();
+ else
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " ";
+ break;
+ }
+ }
+ if (!isPreCoded) {
+ if (field->needContainer())
+ output << "QList<" << m_prefix << typeName << "> ";
+ else if (field->recursive())
+ output << "std::optional<" << m_prefix << typeName << "> ";
+ else if (isEnumeration)
+ output << m_prefix << "::" << typeName << " ";
+ else
+ output << m_prefix << typeName << " ";
+ }
+ }
+
+ const auto lowerCaseFieldName = field->lowerFirstName();
+ output << lowerCaseFieldName;
+ if (!field->needContainer()) {
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (field->typeName().contains(StringIdentifier::integerIdentifier)
+ || field->typeName().contains(StringIdentifier::doubleIdentifier)
+ || field->typeName().contains(StringIdentifier::floatIdentifier)
+ || field->typeName().contains(StringIdentifier::byteIdentifier)
+ || field->typeName().contains(StringIdentifier::sbyteIdentifier))
+ output << " {0}";
+ else if (field->typeName().contains(StringIdentifier::booleanIdentifier)
+ || field->typeName().contains(StringIdentifier::bitIdentifier))
+ output << " {false}";
+ } else if (field->isEnum()) {
+ const auto enumType = std::find_if(m_enumeratedTypes.constBegin(), m_enumeratedTypes.constEnd(),
+ [field](EnumeratedType *e) { return e->name() == field->typeNameSecondPart(); });
+ const auto firstValueName = enumType != m_enumeratedTypes.constEnd() && !(*enumType)->values().empty()
+ ? (*enumType)->values().first()->name() : "Unknown";
+ if (!firstValueName.isEmpty())
+ output << " {" << m_prefix << "::" << field->typeNameSecondPart() << "::" << firstValueName << "}";
+ else
+ output << " {}";
+ } else {
+ if (field->typeName().contains(StringIdentifier::uaStatusCodeIdentifier))
+ output << " {QOpcUa::UaStatusCode::Good}";
+ }
+ }
+ output << ";"
+ << Util::lineBreak();
+ } else {
+ const auto fieldNameLowerCase = field->lowerFirstName();
+ output << Util::indent(1) << m_prefix << structuredType->name() << "::" << field->name()
+ << " " << fieldNameLowerCase << " = " << m_prefix << structuredType->name()
+ << "::" << field->name() << "::"
+ << "None";
+
+ output << ";"
+ << Util::lineBreak();
+ }
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppConstructors(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << m_prefix << structuredType->name() << "::" << m_prefix << structuredType->name()
+ << "()"
+ << Util::lineBreak();
+ output << " : data(new " << m_prefix << structuredType->name() << "Data)\n";
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+
+ output << "/*!"
+ << Util::lineBreak();
+ output << " Constructs a " << structuredType->name() << " from \\a rhs"
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+ output << m_prefix << structuredType->name() << "::" << m_prefix << structuredType->name()
+ << "(const " << m_prefix << structuredType->name() << " &rhs)"
+ << Util::lineBreak();
+ output << " : data(rhs.data)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppOperatorAssignment(
+ const StructuredType *structuredType, QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << " Sets the values from \\a rhs in this " << structuredType->name() << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+ output << m_prefix << structuredType->name() << " &" << m_prefix << structuredType->name()
+ << "::operator=(const " << m_prefix << structuredType->name() << " &rhs)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "if (this != &rhs)"
+ << Util::lineBreak();
+ output << Util::indent(2) << "data.operator=(rhs.data);"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return *this;"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppOperatorEquality(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << Util::indent(1) << "Returns \\c true if this " << structuredType->name()
+ << " has the same value as \\a rhs"
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+
+ output << "bool " << m_prefix << structuredType->name() << "::operator==(const " << m_prefix
+ << structuredType->name() << " &rhs) const"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ QList<Field *> unionSwitchfield;
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleMember->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleMember))
+ arrayLengthfields.push_back(possibleMember);
+ if (possibleMember->name() == field->switchField()
+ && !unionSwitchfield.contains(possibleMember) && field->isUnion())
+ unionSwitchfield.push_back(possibleMember);
+ }
+ }
+
+ if (!unionSwitchfield.isEmpty()) {
+ const auto switchField = unionSwitchfield.first()->lowerFirstName();
+ output << Util::indent(1) << "if (data->" << switchField << " != " << " rhs.data->" << switchField << ")" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ }
+
+ if (structuredType->containsBitMask()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!field->switchField().isEmpty() && !arrayLengthfields.contains(field)) {
+ const auto switchField = Util::lowerFirstLetter(field->switchField());
+ output << Util::indent(1) << "if (data->" << switchField << " != rhs.data->" << switchField << " || (data->" << switchField << " && ";
+ output << "!(data->" << field->lowerFirstName() << " == rhs.data->" << field->lowerFirstName() << ")";
+ output << "))";
+ output << Util::lineBreak() << Util::indent(2) << "return false;" << Util::lineBreak();
+ }
+ }
+ output << Util::lineBreak();
+ }
+
+ auto counterGeneratedTypes = 0;
+ if (structuredType->hasUnion()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !unionSwitchfield.contains(field) && !field->isInStructuredTypeBitMask()) {
+ if (structuredType->containsBitMask() && !field->switchField().isEmpty())
+ continue;
+
+
+ output << Util::indent(1) << "if (static_cast<qint32>(data->" << unionSwitchfield.first()->lowerFirstName() << ") == " << field->switchValue() << " && ";
+ output << "!(data->" << field->lowerFirstName() << " == rhs.data->" << field->lowerFirstName() << ")";
+ output << ")" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ }
+ }
+
+ output << Util::indent(1) << "return true;" << Util::lineBreak();
+ } else {
+ output << Util::indent(1) << "return ";
+
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !unionSwitchfield.contains(field) && !field->isInStructuredTypeBitMask()) {
+ if (structuredType->containsBitMask() && !field->switchField().isEmpty())
+ continue;
+
+ const auto memberName = field->lowerFirstName();
+ if (counterGeneratedTypes != 0)
+ output << Util::lineBreak() << Util::indent(2) << " && ";
+ output << "data->" << memberName << " == rhs.data->" << memberName;
+ counterGeneratedTypes++;
+ }
+ }
+
+ output << ";" << Util::lineBreak();
+ }
+
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppQVariant(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << " Converts this " << structuredType->name() << " to \\l QVariant"
+ << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+
+ output << m_prefix << structuredType->name() << "::operator QVariant() const"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return QVariant::fromValue(*this);"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDestructor(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << m_prefix << structuredType->name() << "::~" << m_prefix << structuredType->name()
+ << "()"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppGetterSetter(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ writeStructuredTypeCppGetter(structuredType, output);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppGetter(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<Field *> unionMember;
+ if (structuredType->hasUnion()) {
+ for (const auto &possibleUnionMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (field->isUnion()) {
+ if (field->switchField() == possibleUnionMember->name()
+ && !unionMember.contains(possibleUnionMember))
+ unionMember.push_back(possibleUnionMember);
+ }
+ }
+ }
+ }
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleArrayLengthField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleArrayLengthField->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleArrayLengthField))
+ arrayLengthfields.push_back(possibleArrayLengthField);
+ }
+ }
+
+ QList<Field *> switchFields;
+ if (structuredType->hasSwitchfield()) {
+ for (const auto &possibleOptionalMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (!possibleOptionalMember->switchField().isEmpty() && possibleOptionalMember->switchField() == field->name())
+ switchFields.push_back(field);
+ }
+ }
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !(field->isInStructuredTypeBitMask() && !switchFields.contains(field))) {
+ const auto tmpFunctionName = field->lowerFirstName();
+
+ output << "/*!\n";
+ output << Util::indent(1) << "Returns the field " << field->name() << " of this " << structuredType->name() << " object"
+ << Util::lineBreak();
+ output << "*/\n";
+
+ const auto typeName = field->typeNameSecondPart();
+ if (!unionMember.contains(field)) {
+ if (field->needContainer())
+ output << "QList<";
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ output << StringIdentifier::typeNameDataTypeConverter.value(field->typeName());
+ } else {
+ bool isPreCoded = false;
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ if (preCodedType.deEncoderName().isEmpty())
+ output << preCodedType.className();
+ else
+ output << preCodedType.deEncoderName();
+ break;
+ }
+ }
+ if (!isPreCoded) {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1))
+ isEnum = true;
+ }
+ if (isEnum)
+ output << m_prefix << "::" << field->typeName().split(":").at(1);
+ else
+ output << m_prefix << typeName;
+ }
+ }
+ if (field->needContainer())
+ output << ">";
+ output << " ";
+
+ } else {
+ output << m_prefix << structuredType->name() << "::" << field->name() << " ";
+ }
+
+ output << m_prefix << structuredType->name() << "::" << tmpFunctionName << "() const"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ if (!field->isUnion()) {
+ if (field->recursive())
+ if (field->needContainer()) {
+ output << Util::indent(1) << "return data->" << tmpFunctionName << ";"
+ << Util::lineBreak();
+ } else {
+ output << Util::indent(1) << "return data->" << tmpFunctionName << ".value_or(" << m_prefix
+ << field->typeName().split(QChar::fromLatin1(':')).at(1) << "()"
+ << ");"
+ << Util::lineBreak();
+ }
+ else
+ output << Util::indent(1) << "return data->" << tmpFunctionName << ";"
+ << Util::lineBreak();
+ } else {
+ const auto switchfieldToLower = Util::lowerFirstLetter(field->switchField());
+ output << Util::indent(1) << "if (data->" << switchfieldToLower << " == " << m_prefix
+ << structuredType->name() << "::" << field->switchField()
+ << "::" << field->name() << ")"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return data->" << tmpFunctionName << ";"
+ << Util::lineBreak();
+ output << Util::indent(1) << "else"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return {};" << Util::lineBreak();
+ }
+ output << "}"
+ << Util::lineBreak(2);
+ if (!unionMember.contains(field))
+ writeStructuredTypeCppSetter(field, structuredType, output);
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppSetter(const Field *field,
+ const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "/*!"
+ << Util::lineBreak();
+ output << Util::indent(1) << "Sets the field " << field->name() << " of this " << structuredType->name() << " object to \\a "
+ << field->lowerFirstName() << Util::lineBreak();
+ output << "*/"
+ << Util::lineBreak();
+
+ output << "void " << m_prefix << structuredType->name() << "::set" << field->name() << "(";
+ const auto tmpFunctionName = field->lowerFirstName();
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (field->typeName().contains(StringIdentifier::booleanIdentifier)
+ || field->typeName().contains(StringIdentifier::integerIdentifier)
+ || field->typeName().contains(StringIdentifier::floatIdentifier)
+ || field->typeName().contains(StringIdentifier::doubleIdentifier)) {
+ if (field->needContainer())
+ output << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << "> ";
+ else
+ output << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << " ";
+ } else {
+ output << "const ";
+ if (field->needContainer())
+ output << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << "> &";
+ else
+ output << StringIdentifier::typeNameDataTypeConverter.value(field->typeName())
+ << " &";
+ }
+ } else {
+ const auto typeName = field->typeNameSecondPart();
+ bool isPreCoded = false;
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ output << "const ";
+ if (field->needContainer())
+ output << "QList<";
+ if (preCodedType.deEncoderName().isEmpty())
+ output << preCodedType.className();
+ else
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " &";
+ break;
+ }
+ }
+
+ if (!isPreCoded) {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1))
+ isEnum = true;
+ }
+ output << "const ";
+ if (field->needContainer())
+ output << "QList <" << m_prefix << typeName << "> &";
+ else if (isEnum)
+ output << m_prefix << "::" << field->typeName().split(":").at(1) << " &";
+ else
+ output << m_prefix << typeName << " &";
+ }
+ }
+ output << tmpFunctionName << ")"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "data->" << tmpFunctionName << " = " << tmpFunctionName << ";"
+ << Util::lineBreak();
+ if (field->isUnion()) {
+ const auto switchfieldToLower = Util::lowerFirstLetter(field->switchField());
+ output << Util::indent(1) << "data->" << switchfieldToLower << " = " << m_prefix << structuredType->name()
+ << "::" << field->switchField() << "::" << field->name() << ";"
+ << Util::lineBreak();
+ }
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeader(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ output << "#pragma once"
+ << Util::lineBreak();
+
+ writeStructuredTypeHeaderIncludes(structuredType, output);
+
+ output << "class " << m_prefix << structuredType->name() << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "public:"
+ << Util::lineBreak();
+
+ writeStructuredTypeHeaderUnion(structuredType, output);
+
+ output << Util::indent(1) << m_prefix << structuredType->name() << "();"
+ << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << structuredType->name() << "(const " << m_prefix
+ << structuredType->name() << " &);"
+ << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << structuredType->name() << " &operator=(const " << m_prefix
+ << structuredType->name() << " &rhs);"
+ << Util::lineBreak();
+ output << Util::indent(1) << "bool operator==(const " << m_prefix << structuredType->name() << " &rhs) const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "inline bool operator!=(const " << m_prefix << structuredType->name() << " &rhs) const"
+ << Util::lineBreak(1)
+ << Util::indent(2) << "{ return !(*this == rhs); }"
+ << Util::lineBreak();
+ output << Util::indent(1) << "operator QVariant() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "~" << m_prefix << structuredType->name() << "();"
+ << Util::lineBreak(2);
+
+ writeStructuredTypeHeaderGetterSetter(structuredType, output);
+
+ writeStructuredTypeHeaderDebug(structuredType, output);
+
+ output << "private:"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QSharedDataPointer<" << m_prefix << structuredType->name() << "Data> data;"
+ << Util::lineBreak();
+ output << "};"
+ << Util::lineBreak(2);
+ output << "Q_DECLARE_METATYPE(" << m_prefix << structuredType->name() << ")"
+ << Util::lineBreak();
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderIncludes(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<QString> qtCoreIncludes{"#include <QSharedData>\n",
+ "#include <QVariant>\n"};
+ QList<QString> qtOpcUaIncludes{};
+ QList<QString> qtClassIncludes;
+ QList<QString> qtPredifinedClasses;
+
+ for (const auto &field : structuredType->fields()) {
+ const auto typeName = field->typeNameSecondPart();
+ QString include;
+ if (field->typeName().startsWith(QStringLiteral("%1:").arg(StringIdentifier::binaryTypeIdentifier))) {
+ if (typeName == StringIdentifier::datetimeIdentifier) {
+ include = "#include <QDateTime>\n";
+ } else if (typeName == StringIdentifier::byteStringIdentifier) {
+ include = "#include <QByteArray>\n";
+ } else if (typeName == StringIdentifier::charArrayIdentifier) {
+ include = "#include <QString>\n";
+ } else if (typeName == StringIdentifier::guidIdentifier) {
+ include = "#include <QUuid>\n";
+ }
+
+ if (!qtCoreIncludes.contains(include)) {
+ qtCoreIncludes.push_back(include);
+ }
+ } else {
+ bool isPrecoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(typeName)) {
+ isPrecoded = true;
+ if (!precodedType.filename().isEmpty()) {
+ include = QStringLiteral("#include <QtOpcUa/%1>\n")
+ .arg(precodedType.filename());
+ break;
+ }
+ }
+ }
+ if (typeName != structuredType->name()) {
+ if (!isPrecoded) {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == typeName)
+ isEnum = true;
+ }
+ if (isEnum) {
+ if (!qtClassIncludes.contains(
+ QStringLiteral("#include \"%1enumerations.h\"\n")
+ .arg(m_prefix.toLower())))
+ qtClassIncludes.push_back(
+ QStringLiteral("#include \"%1enumerations.h\"\n")
+ .arg(m_prefix.toLower()));
+
+ } else {
+ bool isMutualInclude = false;
+ for (const auto &type : m_generateMapping) {
+ StructuredType *tempStructuredType = dynamic_cast<StructuredType *>(
+ type);
+ if (tempStructuredType) {
+ if (tempStructuredType == structuredType)
+ continue;
+ for (const auto &tempField : tempStructuredType->fields()) {
+ const auto tempTypeName = tempField->typeNameSecondPart();
+ if (typeName == tempStructuredType->name()
+ && tempTypeName == structuredType->name()) {
+ isMutualInclude = true;
+ if (!qtPredifinedClasses.contains(
+ QStringLiteral("%1%2")
+ .arg(m_prefix, tempStructuredType->name())))
+ qtPredifinedClasses.push_back(
+ QStringLiteral("%1%2")
+ .arg(m_prefix, tempStructuredType->name()));
+ }
+ }
+ }
+ }
+
+ if (!qtClassIncludes.contains(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), typeName.toLower()))
+ && !isMutualInclude
+ && typeName != StringIdentifier::xmlElementIdentifier)
+ qtClassIncludes.push_back(
+ QStringLiteral("#include \"%1%2.h\"\n")
+ .arg(m_prefix.toLower(), typeName.toLower()));
+ }
+ } else {
+ if (!qtOpcUaIncludes.contains(include)) {
+ qtOpcUaIncludes.push_back(include);
+ }
+ }
+ }
+ }
+ if (field->needContainer()) {
+ include = "#include <QList>\n";
+ if (!qtCoreIncludes.contains(include)) {
+ qtCoreIncludes.push_back(include);
+ }
+ }
+ }
+ if (!qtClassIncludes.isEmpty()) {
+ std::sort(qtClassIncludes.begin(), qtClassIncludes.end());
+ for (const auto &include : qtClassIncludes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+ }
+ std::sort(qtOpcUaIncludes.begin(), qtOpcUaIncludes.end());
+ for (const auto &include : qtOpcUaIncludes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+ std::sort(qtCoreIncludes.begin(), qtCoreIncludes.end());
+ for (const auto &include : qtCoreIncludes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+
+ if (!qtPredifinedClasses.isEmpty()) {
+ std::sort(qtPredifinedClasses.begin(), qtPredifinedClasses.end());
+ for (const auto &predefinedClass : qtPredifinedClasses) {
+ output << "class " << predefinedClass << ";"
+ << Util::lineBreak();
+ }
+ }
+
+ output << "class " << m_prefix << structuredType->name() << "Data;"
+ << Util::lineBreak();
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderUnion(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ if (structuredType->hasUnion()) {
+ QList<Field *> unions;
+ QList<Field *> switchFields;
+ for (const auto &field : structuredType->fields()) {
+ if (field->isUnion())
+ switchFields.push_back(field);
+ }
+ for (const auto &switchField : switchFields) {
+ for (const auto &structuredTypeField : structuredType->fields()) {
+ if (switchField->switchField() == structuredTypeField->name()) {
+ if (!unions.contains(structuredTypeField))
+ unions.push_back(structuredTypeField);
+ }
+ }
+ }
+ QList<Field *> arrayLengthfields;
+ for (const auto &possibleArrayLengthField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleArrayLengthField->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleArrayLengthField))
+ arrayLengthfields.push_back(possibleArrayLengthField);
+ }
+ }
+ for (const auto &field : unions) {
+ output << Util::indent(1) << "enum class " << field->name() << " {"
+ << Util::lineBreak();
+ output << Util::indent(2) << "None = 0,\n";
+ for (int i = 0; i < switchFields.size(); i++) {
+ const int nextField = i + 1;
+ if (!arrayLengthfields.contains(switchFields.at(i))) {
+ if (nextField >= switchFields.size()) {
+ if (switchFields.at(i)->switchField() == field->name()) {
+ output << Util::indent(2) << switchFields.at(i)->name() << " = "
+ << switchFields.at(i)->switchValue() << Util::lineBreak();
+ output << Util::indent(1) << "};"
+ << Util::lineBreak(2);
+ }
+ } else {
+ if (field->name() == switchFields.at(nextField)->switchField()) {
+ output << Util::indent(2) << switchFields.at(i)->name() << " = "
+ << switchFields.at(i)->switchValue() << ","
+ << Util::lineBreak();
+ } else {
+ output << Util::indent(2) << switchFields.at(i)->name() << " = "
+ << switchFields.at(i)->switchValue() << Util::lineBreak();
+ output << Util::indent(1) << "};"
+ << Util::lineBreak(2);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderGetterSetter(const StructuredType *structuredType,
+ QTextStream &output)
+{
+ QList<Field *> unionMember;
+ QList<Field *> arrayLengthfields;
+ QList<Field *> switchFields;
+ for (const auto &possibleMember : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleMember->name() == field->lengthField()
+ && !arrayLengthfields.contains(possibleMember))
+ arrayLengthfields.push_back(possibleMember);
+ if (field->isUnion() && field->switchField() == possibleMember->name()
+ && !unionMember.contains(possibleMember))
+ unionMember.push_back(possibleMember);
+ if (!possibleMember->switchField().isEmpty() && field->name() == possibleMember->switchField())
+ switchFields.push_back(field);
+ }
+ }
+ for (const auto &field : structuredType->fields()) {
+ if (!arrayLengthfields.contains(field) && !(field->isInStructuredTypeBitMask() && !switchFields.contains(field))) {
+ const auto typeName = field->typeNameSecondPart();
+ const auto tmpFunctionName = field->lowerFirstName();
+ if (!unionMember.contains(field)) {
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ if (field->typeName().contains(StringIdentifier::booleanIdentifier)
+ || field->typeName().contains(StringIdentifier::integerIdentifier)
+ || field->typeName().contains(StringIdentifier::floatIdentifier)
+ || field->typeName().contains(StringIdentifier::doubleIdentifier)) {
+ if (!field->needContainer()) {
+ output << Util::indent(1)
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "("
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " " << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1) << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> " << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ } else {
+ if (!field->needContainer()) {
+ output << Util::indent(1)
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const "
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1) << "QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const QList<"
+ << StringIdentifier::typeNameDataTypeConverter.value(
+ field->typeName())
+ << "> &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ }
+ } else {
+ bool isPreCoded = false;
+ for (const auto &preCodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (preCodedType.contains(typeName)) {
+ isPreCoded = true;
+ if (preCodedType.deEncoderName().isEmpty()) {
+ output << Util::indent(1);
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.className();
+ if (field->needContainer())
+ output << ">";
+ output << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const ";
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.className();
+ if (field->needContainer())
+ output << ">";
+ output << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1);
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const ";
+ if (field->needContainer())
+ output << "QList<";
+ output << preCodedType.deEncoderName();
+ if (field->needContainer())
+ output << ">";
+ output << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ break;
+ }
+ }
+
+ if (!isPreCoded) {
+ if (field->needContainer()) {
+ output << Util::indent(1) << "QList <" << m_prefix << typeName << "> "
+ << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const QList <"
+ << m_prefix << typeName << "> &" << tmpFunctionName
+ << ");"
+ << Util::lineBreak(2);
+ } else {
+ bool isEnum = false;
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ if (enumeratedType->name() == field->typeName().split(":").at(1))
+ isEnum = true;
+ }
+ if (isEnum) {
+ output << Util::indent(1) << m_prefix << "::" << typeName << " "
+ << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const " << m_prefix
+ << "::" << typeName << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ } else {
+ output << Util::indent(1) << m_prefix << typeName << " "
+ << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "void set" << field->name() << "(const " << m_prefix
+ << typeName << " &" << tmpFunctionName << ");"
+ << Util::lineBreak(2);
+ }
+ }
+ }
+ }
+ } else { // is union switchfield, create just getter
+ output << Util::indent(1) << field->name() << " " << tmpFunctionName << "() const;"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ }
+ }
+ }
+}
+
+void DataTypeFileWriter::writeStructuredTypeHeaderDebug(const StructuredType *structuredType, QTextStream &output)
+{
+ output << Util::indent(1) << "friend QDebug operator<<(QDebug debug, const " << m_prefix << structuredType->name() << " &v);";
+ output << Util::lineBreak(2);
+}
+
+void DataTypeFileWriter::writeStructuredTypeCppDebug(const StructuredType *structuredType, QTextStream &output)
+{
+ const auto typeName = QStringLiteral("%1%2").arg(m_prefix, structuredType->name());
+
+ output << "/*!" << Util::lineBreak();
+ output << Util::indent(1) << "Prints the field values of object \\a v to \\a debug" << Util::lineBreak();
+ output << "*/" << Util::lineBreak();
+ output << "QDebug operator<<(QDebug debug, const " << typeName << " &v)" << Util::lineBreak();
+ output << "{" << Util::lineBreak();
+ output << Util::indent(1) << "QDebugStateSaver saver(debug);" << Util::lineBreak();
+ output << Util::indent(1) << "debug.nospace().noquote();" << Util::lineBreak(2);
+ output << Util::indent(1) << "debug << \"" << typeName << " (\";" << Util::lineBreak(2);
+
+ if (!structuredType->hasUnion() && structuredType->hasSwitchfield())
+ output << Util::indent(1) << "bool firstFieldPrinted = false;" << Util::lineBreak(2);
+
+ bool isFirst = true;
+
+ QSet<Field *> lengthFields;
+
+ for (const auto &field : structuredType->fields()) {
+ for (const auto &innerField : structuredType->fields()) {
+ if (!field->lengthField().isEmpty() && field->lengthField() == innerField->name())
+ lengthFields.insert(innerField);
+ }
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (structuredType->hasUnion() && field == structuredType->fields().constFirst())
+ continue;
+
+ if (field->isInStructuredTypeBitMask())
+ continue;
+
+ if (lengthFields.contains(field))
+ continue;
+
+ if (structuredType->hasUnion())
+ output << Util::indent(1) << "if (static_cast<quint32>(v." << Util::lowerFirstLetter(field->switchField()) << "()) == " << field->switchValue() << ") {" << Util::lineBreak();
+ else if (!field->switchField().isEmpty())
+ output << Util::indent(1) << "if (v." << Util::lowerFirstLetter(field->switchField()) << "()) {" << Util::lineBreak();
+
+ int indentOffset = 0;
+ if (structuredType->hasUnion() || !field->switchField().isEmpty())
+ indentOffset = 1;
+
+ if (!structuredType->hasUnion() && !isFirst) {
+ if (structuredType->hasSwitchfield())
+ output << Util::indent(1 + indentOffset) << "if (firstFieldPrinted)" << Util::lineBreak();
+ output << Util::indent(1 + (structuredType->hasSwitchfield() ? indentOffset + 1 : 0)) << "debug << \", \";" << Util::lineBreak();
+ }
+
+ output << Util::indent(1 + indentOffset) << "debug << \"" << field->name() << ": \";" << Util::lineBreak();
+
+ // Not all types have an operator<< for QDebug...
+ const auto isPrecoded = std::find_if(StringIdentifier::opcUaPrecodedTypes.constBegin(), StringIdentifier::opcUaPrecodedTypes.constEnd(),
+ [&](const auto &entry) { return entry.name() == field->typeNameSecondPart(); });
+
+ if (isPrecoded == StringIdentifier::opcUaPrecodedTypes.constEnd() || StringIdentifier::precodedTypesWithDebugOperator.contains(field->typeNameSecondPart())) {
+ output << Util::indent(1 + indentOffset) << "debug << v." << field->lowerFirstName() << "();" << Util::lineBreak();
+ } else {
+ if (field->needContainer()) {
+ const auto tempListName = QStringLiteral("%1%2").arg(field->lowerFirstName(), "Strings");
+ output << Util::indent(1 + indentOffset) << "QStringList " << tempListName << ";" << Util::lineBreak();
+ output << Util::indent(1 + indentOffset) << "for (int i = 0; i < v." << field->lowerFirstName() << "().size(); ++i)" << Util::lineBreak();
+ output << Util::indent(2 + indentOffset) << tempListName << ".push_back(\"" << isPrecoded->className() << "(...)\"" << ");" << Util::lineBreak();
+ output << Util::indent(1 + indentOffset) << "debug << \"QList(\" << " << tempListName << ".join(\", \") << \")\";";
+ } else {
+ output << Util::indent(1 + indentOffset) << "debug << \"" << isPrecoded->className() << "(...)\";";
+ }
+ output << Util::lineBreak();
+ }
+
+ if (!structuredType->hasUnion() && structuredType->hasSwitchfield() && field != structuredType->fields().constLast())
+ output << Util::indent(1 + indentOffset) << "firstFieldPrinted = true;" << Util::lineBreak();
+
+ if (structuredType->hasUnion() || !field->switchField().isEmpty())
+ output << Util::indent(1) << "}" << Util::lineBreak();
+
+ output << Util::lineBreak();
+
+ isFirst = false;
+ }
+
+ output << Util::indent(1) << "debug << \")\";" << Util::lineBreak();
+ output << Util::indent(1) << "return debug;" << Util::lineBreak();
+ output << "}" << Util::lineBreak();
+}
+
+DataTypeFileWriter::GeneratingError DataTypeFileWriter::writeEnumeratedTypes()
+{
+ QFile file;
+ const auto fileName = QStringLiteral("%1enumerations.h").arg(m_prefix.toLower());
+ QDir dir(m_path);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnableToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+ writeLicenseHeader(output);
+
+ if (!m_generatedEnumeratedTypeFilenames.contains(
+ QStringLiteral("%1enumerations").arg(m_prefix.toLower())))
+ m_generatedEnumeratedTypeFilenames.push_back(
+ QStringLiteral("%1enumerations").arg(m_prefix.toLower()));
+ output << "#pragma once"
+ << Util::lineBreak();
+ output << "#include <QMetaType>"
+ << Util::lineBreak(2);
+ output << "namespace " << m_prefix << " {"
+ << Util::lineBreak();
+ output << "Q_NAMESPACE"
+ << Util::lineBreak(2);
+ for (const auto &enumeratedType : m_enumeratedTypes) {
+ output << "enum class " << enumeratedType->name() << " {" << Util::lineBreak(1);
+ for (const auto &enumeratedValue : enumeratedType->values()) {
+ output << Util::indent(1) << enumeratedValue->name() << " = " << enumeratedValue->value();
+ if (!enumeratedType->values().endsWith(enumeratedValue))
+ output << ",";
+ output << Util::lineBreak();
+ }
+ output << "};"
+ << Util::lineBreak();
+ output << "Q_ENUM_NS(" << enumeratedType->name() << ")"
+ << Util::lineBreak(2);
+ }
+
+ output << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak();
+ return GeneratingError::NoError;
+}
+
+DataTypeFileWriter::GeneratingError DataTypeFileWriter::generateFile(const XmlElement *type,
+ const QString &fileExtension)
+{
+ QFile file;
+ const auto fileName = QStringLiteral("%1%2%3").arg(m_prefix.toLower(),
+ type->name().toLower(),
+ fileExtension);
+ QDir dir(m_path);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnableToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+ writeLicenseHeader(output);
+
+ auto structuredType = dynamic_cast<const StructuredType *>(type);
+ if (structuredType) {
+ if (fileExtension == StringIdentifier::headerIdentifier) {
+ writeStructuredTypeHeader(structuredType, output);
+ } else {
+ if (fileExtension == StringIdentifier::cppIdentifier) {
+ writeStructuredTypeCpp(structuredType, output);
+ }
+ }
+ }
+ file.close();
+ return NoError;
+}
+
+DataTypeFileWriter::GeneratingError DataTypeFileWriter::generateTypes(
+ const QList<XmlElement *> &types)
+{
+ m_generateMapping.append(types);
+ for (const auto &type : m_generateMapping) {
+ const auto enumeratedType = dynamic_cast<EnumeratedType *>(type);
+ if (enumeratedType)
+ if (!m_enumeratedTypes.contains(enumeratedType))
+ m_enumeratedTypes.append(enumeratedType);
+ }
+ if (writeEnumeratedTypes() != GeneratingError::NoError) {
+ return GeneratingError::UnableToWrite;
+ }
+
+ for (const auto &type : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(type);
+ if (structuredType) {
+ if (generateFile(type, StringIdentifier::headerIdentifier) != GeneratingError::NoError)
+ return GeneratingError::UnableToWrite;
+ if (generateFile(type, StringIdentifier::cppIdentifier) != GeneratingError::NoError)
+ return GeneratingError::UnableToWrite;
+ }
+ }
+ return NoError;
+}
+
+QList<XmlElement *> DataTypeFileWriter::generateMapping() const
+{
+ return m_generateMapping;
+}
+
+void DataTypeFileWriter::setGenerateMapping(const QList<XmlElement *> &generateMapping)
+{
+ m_generateMapping = generateMapping;
+}
diff --git a/tools/datatypecodegenerator/datatypefilewriter.h b/tools/datatypecodegenerator/datatypefilewriter.h
new file mode 100644
index 0000000..aca22a7
--- /dev/null
+++ b/tools/datatypecodegenerator/datatypefilewriter.h
@@ -0,0 +1,80 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "visitor.h"
+
+#include <QtCore/qlist.h>
+#include <QtCore/qtextstream.h>
+
+class DataTypeFileWriter : public Visitor
+{
+public:
+ enum GeneratingError { NoError, UnableToWrite };
+ DataTypeFileWriter(const QString &path,
+ const QString &prefix,
+ const QString &header);
+ ~DataTypeFileWriter() override = default;
+
+ void visit(EnumeratedType *enumeratedType) override;
+ void visit(EnumeratedValue *enumeratedValue) override;
+ void visit(Field *field) override;
+ void visit(Import *import) override;
+ void visit(StructuredType *structuredType) override;
+ void visit(TypeDictionary *typeDictionary) override;
+ void visit(XmlElement *xmlElement) override;
+
+ GeneratingError generateFile(const XmlElement *type, const QString &fileExtension);
+ GeneratingError generateTypes(const QList<XmlElement *> &types);
+
+ QList<XmlElement *> generateMapping() const;
+ void setGenerateMapping(const QList<XmlElement *> &generateMapping);
+
+private:
+ QString m_path;
+ QString m_prefix;
+ QString m_header;
+ QList<QString> m_generatedEnumeratedTypeFilenames;
+ QList<QString> m_generatedStructuredTypeFilenames;
+ QList<XmlElement *> m_generateMapping;
+ QList<EnumeratedType *> m_enumeratedTypes;
+
+ void writeLicenseHeader(QTextStream &output);
+
+ void writeStructuredTypeCpp(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppClassComment(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppDataClass(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppDataClassMember(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppConstructors(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppOperatorAssignment(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppOperatorEquality(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppQVariant(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppDestructor(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppGetterSetter(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppGetter(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeCppSetter(const Field *field,
+ const StructuredType *structuredType,
+ QTextStream &output);
+
+ void writeStructuredTypeHeader(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeHeaderIncludes(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeHeaderUnion(const StructuredType *structuredType, QTextStream &output);
+ void writeStructuredTypeHeaderGetterSetter(const StructuredType *structuredType,
+ QTextStream &output);
+
+ void writeStructuredTypeHeaderDebug(const StructuredType *structuredType,
+ QTextStream &output);
+ void writeStructuredTypeCppDebug(const StructuredType *structuredType,
+ QTextStream &output);
+
+ void writeEnumeratedType(const EnumeratedType *enumeratedType, QTextStream &output);
+ GeneratingError writeEnumeratedTypes();
+};
diff --git a/tools/datatypecodegenerator/dependencydatatypevalidator.cpp b/tools/datatypecodegenerator/dependencydatatypevalidator.cpp
new file mode 100644
index 0000000..243fd14
--- /dev/null
+++ b/tools/datatypecodegenerator/dependencydatatypevalidator.cpp
@@ -0,0 +1,107 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "dependencydatatypevalidator.h"
+#include "enumeratedtype.h"
+#include "field.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+
+DependencyDataTypeValidator::DependencyDataTypeValidator()
+ : m_readResolveDependencies(ReadDependencies)
+{}
+
+void DependencyDataTypeValidator::visit(EnumeratedType *enumeratedType)
+{
+ if (m_readResolveDependencies == DependencyDataTypeValidator::ResolveDependencies) {
+ if (m_unresolvedDependencyStringList.contains(enumeratedType->name())) {
+ m_unresolvedDependencyStringList.removeAll(enumeratedType->name());
+ m_resolvedDependencyElementList.push_back(enumeratedType);
+ }
+ }
+}
+
+void DependencyDataTypeValidator::visit(EnumeratedValue *enumeratedValue)
+{
+ Q_UNUSED(enumeratedValue);
+}
+
+void DependencyDataTypeValidator::visit(Field *field)
+{
+ if (m_readResolveDependencies == DependencyDataTypeValidator::ReadDependencies) {
+ if (!field->typeName().contains("opc:")) {
+ const auto typeName = field->typeNameSecondPart();
+ for (const auto &precoded : StringIdentifier::opcUaPrecodedTypes) {
+ if (precoded.contains(typeName)) {
+ return;
+ }
+ }
+ m_unresolvedDependencyStringList.push_back(typeName);
+ }
+ }
+}
+
+void DependencyDataTypeValidator::visit(Import *import)
+{
+ Q_UNUSED(import);
+}
+
+void DependencyDataTypeValidator::visit(StructuredType *structuredType)
+{
+ if (m_readResolveDependencies == DependencyDataTypeValidator::ResolveDependencies) {
+ if (m_unresolvedDependencyStringList.contains(structuredType->name())) {
+ m_unresolvedDependencyStringList.removeAll(structuredType->name());
+ m_resolvedDependencyElementList.push_back(structuredType);
+ for (const auto &field : structuredType->fields()) {
+ const auto typeName = field->typeNameSecondPart();
+
+ if (!StringIdentifier::typeNameDataTypeConverter.contains(field->typeName())) {
+ bool isPrecoded = false;
+ for (const auto &precoded : StringIdentifier::opcUaPrecodedTypes) {
+ if (precoded.contains(typeName)) {
+ isPrecoded = true;
+ break;
+ }
+ }
+ if (!isPrecoded && !m_unresolvedDependencyStringList.contains(typeName)) {
+ bool isResolved = false;
+ for (const auto &type : m_resolvedDependencyElementList) {
+ if (type->name() == typeName) {
+ isResolved = true;
+ break;
+ }
+ }
+ if (!isResolved)
+ m_unresolvedDependencyStringList.push_back(typeName);
+ }
+ }
+ }
+ }
+ }
+}
+
+void DependencyDataTypeValidator::visit(TypeDictionary *typeDictionary)
+{
+ Q_UNUSED(typeDictionary);
+}
+
+void DependencyDataTypeValidator::visit(XmlElement *xmlElement)
+{
+ Q_UNUSED(xmlElement);
+}
+
+QStringList DependencyDataTypeValidator::unresolvedDependencyStringList() const
+{
+ return m_unresolvedDependencyStringList;
+}
+
+QList<XmlElement *> DependencyDataTypeValidator::resolvedDependencyElementList() const
+{
+ return m_resolvedDependencyElementList;
+}
+
+void DependencyDataTypeValidator::setReadResolveDependencies(
+ const ReadResolveDependencies &readResolveDependencies)
+{
+ m_readResolveDependencies = readResolveDependencies;
+}
diff --git a/tools/datatypecodegenerator/dependencydatatypevalidator.h b/tools/datatypecodegenerator/dependencydatatypevalidator.h
new file mode 100644
index 0000000..aad3e4e
--- /dev/null
+++ b/tools/datatypecodegenerator/dependencydatatypevalidator.h
@@ -0,0 +1,38 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "visitor.h"
+
+#include <QtCore/qlist.h>
+
+class XmlElement;
+
+class DependencyDataTypeValidator : public Visitor
+{
+public:
+ enum ReadResolveDependencies { ReadDependencies, ResolveDependencies };
+
+ DependencyDataTypeValidator();
+ ~DependencyDataTypeValidator() override = default;
+
+ void visit(EnumeratedType *enumeratedType) override;
+ void visit(EnumeratedValue *enumeratedValue) override;
+ void visit(Field *field) override;
+ void visit(Import *import) override;
+ void visit(StructuredType *structuredType) override;
+ void visit(TypeDictionary *typeDictionary) override;
+ void visit(XmlElement *xmlElement) override;
+
+ QStringList unresolvedDependencyStringList() const;
+
+ QList<XmlElement *> resolvedDependencyElementList() const;
+
+ void setReadResolveDependencies(const ReadResolveDependencies &readResolveDependencies);
+
+private:
+ ReadResolveDependencies m_readResolveDependencies;
+ QList<XmlElement *> m_resolvedDependencyElementList;
+ QStringList m_unresolvedDependencyStringList;
+};
diff --git a/tools/datatypecodegenerator/enumeratedtype.cpp b/tools/datatypecodegenerator/enumeratedtype.cpp
new file mode 100644
index 0000000..b31ed43
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedtype.cpp
@@ -0,0 +1,58 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "enumeratedtype.h"
+#include "enumeratedvalue.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+EnumeratedType::EnumeratedType(const QString &name, quint32 lengthInBits)
+ : XmlElement(name)
+ , m_lengthInBits(lengthInBits)
+{}
+
+EnumeratedType::~EnumeratedType()
+{
+ qDeleteAll(m_values);
+}
+
+quint32 EnumeratedType::lengthInBits() const
+{
+ return m_lengthInBits;
+}
+
+void EnumeratedType::setLengthInBits(quint32 lengthInBits)
+{
+ m_lengthInBits = lengthInBits;
+}
+
+void EnumeratedType::addValue(EnumeratedValue *enumeratedValue)
+{
+ m_values.push_back(enumeratedValue);
+}
+
+void EnumeratedType::print() const
+{
+ XmlElement::print();
+ qDebug() << "LengthInBits: " << m_lengthInBits;
+ for (int i = 0; i < m_values.size(); i++)
+ m_values.at(i)->print();
+}
+
+void EnumeratedType::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+ for (auto &enumeratedValues : m_values)
+ enumeratedValues->accept(visitor);
+}
+
+QList<EnumeratedValue *> EnumeratedType::values() const
+{
+ return m_values;
+}
+
+void EnumeratedType::setValues(const QList<EnumeratedValue *> &values)
+{
+ m_values = values;
+}
diff --git a/tools/datatypecodegenerator/enumeratedtype.h b/tools/datatypecodegenerator/enumeratedtype.h
new file mode 100644
index 0000000..7e212e6
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedtype.h
@@ -0,0 +1,34 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+#include <QtCore/qlist.h>
+
+class EnumeratedValue;
+class Visitor;
+
+class EnumeratedType : public XmlElement
+
+{
+public:
+ EnumeratedType(const QString &name, quint32 lengthInBits);
+ ~EnumeratedType();
+
+ void addValue(EnumeratedValue *enumeratedValue);
+ void print() const override;
+
+ quint32 lengthInBits() const;
+ void setLengthInBits(quint32 lengthInBits);
+
+ QList<EnumeratedValue *> values() const;
+ void setValues(const QList<EnumeratedValue *> &values);
+
+ virtual void accept(Visitor *visitor) override;
+
+private:
+ quint32 m_lengthInBits;
+ QList<EnumeratedValue *> m_values;
+};
diff --git a/tools/datatypecodegenerator/enumeratedvalue.cpp b/tools/datatypecodegenerator/enumeratedvalue.cpp
new file mode 100644
index 0000000..f28769c
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedvalue.cpp
@@ -0,0 +1,33 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "enumeratedvalue.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+EnumeratedValue::EnumeratedValue(const QString &name, qint32 value)
+ : XmlElement(name)
+ , m_value(value)
+{}
+
+void EnumeratedValue::print() const
+{
+ XmlElement::print();
+ qDebug() << "Value: " << m_value;
+}
+
+void EnumeratedValue::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+}
+
+qint32 EnumeratedValue::value() const
+{
+ return m_value;
+}
+
+void EnumeratedValue::setValue(qint32 value)
+{
+ m_value = value;
+}
diff --git a/tools/datatypecodegenerator/enumeratedvalue.h b/tools/datatypecodegenerator/enumeratedvalue.h
new file mode 100644
index 0000000..fc1d85c
--- /dev/null
+++ b/tools/datatypecodegenerator/enumeratedvalue.h
@@ -0,0 +1,24 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class Visitor;
+
+class EnumeratedValue : public XmlElement
+{
+public:
+ EnumeratedValue(const QString &name, qint32 value);
+ ~EnumeratedValue() override = default;
+
+ void print() const override;
+ virtual void accept(Visitor *visitor) override;
+
+ qint32 value() const;
+ void setValue(qint32 value);
+
+private:
+ qint32 m_value;
+};
diff --git a/tools/datatypecodegenerator/field.cpp b/tools/datatypecodegenerator/field.cpp
new file mode 100644
index 0000000..97499a9
--- /dev/null
+++ b/tools/datatypecodegenerator/field.cpp
@@ -0,0 +1,142 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "field.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qstringlist.h>
+
+Field::Field(const QString &name, const QString &typeName, const QString &lengthField)
+ : XmlElement(name)
+ , m_lengthField(lengthField)
+ , m_typeName(typeName)
+{}
+
+QString Field::typeName() const
+{
+ return m_typeName;
+}
+
+QString Field::typeNameSecondPart() const
+{
+ return m_typeName.split(':').value(1, QString());
+}
+
+void Field::setTypeName(const QString &typeName)
+{
+ m_typeName = typeName;
+}
+
+QString Field::lengthField() const
+{
+ return m_lengthField;
+}
+
+void Field::setLengthField(const QString &lengthField)
+{
+ m_lengthField = lengthField;
+}
+
+bool Field::hasLengthField() const
+{
+ return m_hasLengthField;
+}
+
+void Field::setHasLengthField(bool hasLengthField)
+{
+ m_hasLengthField = hasLengthField;
+}
+
+bool Field::needContainer() const
+{
+ return m_needContainer;
+}
+
+void Field::setNeedContainer(bool needContainer)
+{
+ m_needContainer = needContainer;
+}
+
+bool Field::isInStructuredTypeBitMask() const
+{
+ return m_isInStructuredTypeBitMask;
+}
+
+void Field::setIsInStructuredTypeBitMask(bool isInStructuredTypeBitMask)
+{
+ m_isInStructuredTypeBitMask = isInStructuredTypeBitMask;
+}
+
+int Field::length() const
+{
+ return m_length;
+}
+
+void Field::setLength(int length)
+{
+ m_length = length;
+}
+
+QString Field::switchField() const
+{
+ return m_switchField;
+}
+
+void Field::setSwitchField(const QString &switchField)
+{
+ m_switchField = switchField;
+}
+
+int Field::switchValue() const
+{
+ return m_switchValue;
+}
+
+void Field::setSwitchValue(int switchValue)
+{
+ m_switchValue = switchValue;
+}
+
+bool Field::isUnion() const
+{
+ return m_isUnion;
+}
+
+void Field::setIsUnion(bool isUnion)
+{
+ m_isUnion = isUnion;
+}
+
+bool Field::recursive() const
+{
+ return m_recursive;
+}
+
+void Field::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
+
+bool Field::isEnum() const
+{
+ return m_isEnum;
+}
+
+void Field::setIsEnum(bool newIsEnum)
+{
+ m_isEnum = newIsEnum;
+}
+
+void Field::print() const
+{
+ XmlElement::print();
+ qDebug() << "Type name: " << m_typeName;
+ if (!m_lengthField.isEmpty())
+ qDebug() << "Length field: " << m_lengthField;
+}
+
+void Field::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+}
diff --git a/tools/datatypecodegenerator/field.h b/tools/datatypecodegenerator/field.h
new file mode 100644
index 0000000..0e36bcf
--- /dev/null
+++ b/tools/datatypecodegenerator/field.h
@@ -0,0 +1,62 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class Field : public XmlElement
+{
+public:
+ Field(const QString &name, const QString &typeName, const QString &lengthField = QString());
+ ~Field() override = default;
+
+ void print() const override;
+ virtual void accept(Visitor *visitor) override;
+
+ QString typeName() const;
+ QString typeNameSecondPart() const;
+ void setTypeName(const QString &typeName);
+
+ QString lengthField() const;
+ void setLengthField(const QString &lengthField);
+
+ bool hasLengthField() const;
+ void setHasLengthField(bool hasLengthField);
+
+ bool needContainer() const;
+ void setNeedContainer(bool needContainer);
+
+ bool isInStructuredTypeBitMask() const;
+ void setIsInStructuredTypeBitMask(bool isInStructuredTypeBitMask);
+
+ int length() const;
+ void setLength(int length);
+
+ QString switchField() const;
+ void setSwitchField(const QString &switchField);
+
+ int switchValue() const;
+ void setSwitchValue(int switchValue);
+
+ bool isUnion() const;
+ void setIsUnion(bool isUnion);
+
+ bool recursive() const;
+ void setRecursive(bool recursive);
+
+ bool isEnum() const;
+ void setIsEnum(bool newIsEnum);
+private:
+ bool m_hasLengthField{false};
+ bool m_needContainer{false};
+ bool m_isInStructuredTypeBitMask{false};
+ bool m_isUnion{false};
+ int m_length;
+ int m_switchValue{0};
+ bool m_recursive{false};
+ bool m_isEnum{false};
+ QString m_lengthField;
+ QString m_switchField;
+ QString m_typeName;
+};
diff --git a/tools/datatypecodegenerator/import.cpp b/tools/datatypecodegenerator/import.cpp
new file mode 100644
index 0000000..7702430
--- /dev/null
+++ b/tools/datatypecodegenerator/import.cpp
@@ -0,0 +1,42 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "import.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+Import::Import(const QString &nameSpace, const QString &location)
+ : m_location(location)
+ , m_nameSpace(nameSpace)
+{}
+
+QString Import::nameSpace() const
+{
+ return m_nameSpace;
+}
+
+void Import::setNameSpace(const QString &nameSpace)
+{
+ m_nameSpace = nameSpace;
+}
+
+QString Import::location() const
+{
+ return m_location;
+}
+
+void Import::setLocation(const QString &location)
+{
+ m_location = location;
+}
+
+void Import::print() const
+{
+ qDebug() << "Namespace:" << m_nameSpace << "\tLocation:" << m_location;
+}
+
+void Import::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+}
diff --git a/tools/datatypecodegenerator/import.h b/tools/datatypecodegenerator/import.h
new file mode 100644
index 0000000..9343904
--- /dev/null
+++ b/tools/datatypecodegenerator/import.h
@@ -0,0 +1,28 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class Visitor;
+
+class Import : public XmlElement
+{
+public:
+ Import(const QString &nameSpace, const QString &location);
+ ~Import() override = default;
+
+ virtual void print() const override;
+ virtual void accept(Visitor *visitor) override;
+
+ QString nameSpace() const;
+ void setNameSpace(const QString &nameSpace);
+
+ QString location() const;
+ void setLocation(const QString &location);
+
+private:
+ QString m_location;
+ QString m_nameSpace;
+};
diff --git a/tools/datatypecodegenerator/main.cpp b/tools/datatypecodegenerator/main.cpp
new file mode 100644
index 0000000..20ab28a
--- /dev/null
+++ b/tools/datatypecodegenerator/main.cpp
@@ -0,0 +1,181 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "recursivedescentparser.h"
+
+#include <QtCore/qcommandlineoption.h>
+#include <QtCore/qcommandlineparser.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qregularexpression.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qtextstream.h>
+#include <QtCore/qxmlstream.h>
+
+#include <cstdlib>
+
+bool readBsdFile(RecursiveDescentParser &recursiveDescentParser,
+ const QString &fileName,
+ bool dependencyInput)
+{
+ switch (recursiveDescentParser.parseFile(fileName, dependencyInput)) {
+ case RecursiveDescentParser::NoError:
+ return true;
+ case RecursiveDescentParser::InvalidFileName:
+ qCritical() << "Error: File does not exist:" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidTypeDictionaryEntry:
+ qCritical() << "Error: Invalid TypeDictionary entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidStructuredTypeEntry:
+ qCritical() << "Error: Invalid StructuredType entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidEnumeratedTypeEntry:
+ qCritical() << "Error: Invalid EnumeratedType entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidImportEntry:
+ qCritical() << "Error: Invalid Import entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidFieldEntry:
+ qCritical() << "Error: Invalid Field entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::InvalidEnumeratedValueEntry:
+ qCritical() << "Error: Invalid EnumeratedValue entry in" << fileName;
+ return false;
+ case RecursiveDescentParser::CannotFullyGenerateNamespaceZero:
+ qCritical() << "Error: Full generation of namespace 0 is currently not "
+ "supported";
+ return false;
+ case RecursiveDescentParser::MissingDependency:
+ qCritical() << "Error: Missing dependency Type found in" << fileName;
+ return false;
+ default:
+ qCritical() << "Error: Unknown parsing error occurred";
+ return false;
+ }
+}
+
+bool generateBsdFiles(RecursiveDescentParser &recursiveDescentParser,
+ const QString &outputPath,
+ const QString &dataPrefix,
+ const QString &outputFileHeader)
+{
+ switch (recursiveDescentParser.generateInputFiles(outputPath,
+ dataPrefix,
+ outputFileHeader)) {
+ case RecursiveDescentParser::NoError:
+ return true;
+ case RecursiveDescentParser::UnableToWriteFile:
+ qCritical() << "Error: Unable to write files at specified path.";
+ return false;
+ case RecursiveDescentParser::MissingDependency:
+ qCritical() << "Error: Unresolved dependent type occurred.";
+ return false;
+ case RecursiveDescentParser::UnableToResolveDependency:
+ qCritical() << "Error: Unresolvable mapping occurred.";
+ default:
+ qCritical() << "Error: Unknown file generating error occurred.";
+ return false;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ QCoreApplication a(argc, argv);
+
+ const QString appName = QStringLiteral("qopcuaxmldatatypes2cpp");
+ const QString appVersion = QStringLiteral("1.0");
+
+ auto arguments = a.arguments();
+ arguments.replace(0, appName);
+
+ const auto outputFileHeader = QStringLiteral("/*\n"
+ " * This file was generated by %1 version %2\n"
+ " * Command line used: %3\n"
+ " */")
+ .arg(appName,
+ appVersion,
+ arguments.join(QLatin1Char(' ')));
+
+ QCoreApplication::setApplicationName(appName);
+ QCoreApplication::setApplicationVersion(appVersion);
+ QCommandLineParser parser;
+ parser.setApplicationDescription(
+ "Code generator for custom data models.\n"
+ "Converts OPC UA .bsd files into enums and C++ data classes and generates a class "
+ "to decode and encode the values from/to a QOpcUaExtensionObject with binary body or a QByteArray.");
+ parser.addHelpOption();
+ parser.addVersionOption();
+
+ const QCommandLineOption inputFileOption(QStringList() << "i"
+ << "input",
+ "A primary input file. Will generate code for all contained types and "
+ "check for missing dependencies",
+ "file");
+ parser.addOption(inputFileOption);
+ const QCommandLineOption inputDependencyFileOption(
+ QStringList() << "d"
+ << "dependencyinput",
+ "A dependency input file. Only types required by primary input files will be generated",
+ "file");
+ parser.addOption(inputDependencyFileOption);
+ const QCommandLineOption outputDirectoryPathOption(QStringList() << "o"
+ << "output",
+ "output directory for the generated C++ files.",
+ "path");
+ parser.addOption(outputDirectoryPathOption);
+ const QCommandLineOption
+ outputPrefixOption(QStringList() << "p"
+ << "prefix",
+ "prefix for the generated files, default is GeneratedOpcUa",
+ "prefix",
+ "GeneratedOpcUa");
+ parser.addOption(outputPrefixOption);
+
+ parser.process(a);
+
+ if (!parser.isSet(inputFileOption)) {
+ qCritical() << "Error: At least one input file must be specified";
+ parser.showHelp(1);
+ return EXIT_FAILURE;
+ }
+
+ if (!parser.isSet(outputDirectoryPathOption)) {
+ qCritical() << "Error: The output path must be specified.";
+ parser.showHelp(1);
+ return EXIT_FAILURE;
+ }
+
+ if (parser.values(outputPrefixOption).size() > 1)
+ qInfo() << "Info: The first output prefix will be used";
+ if (!parser.values(outputPrefixOption)
+ .at(0)
+ .contains(QRegularExpression("^[A-Za-z]+[A-Za-z0-9]*$"))) {
+ qCritical() << "Error: The prefix contains illegal characters";
+ qInfo() << "Info: The prefix must consist of letters and numbers and start with a letter";
+ return EXIT_FAILURE;
+ }
+
+ const auto dataPrefix = parser.value(outputPrefixOption);
+
+ auto success = true;
+ RecursiveDescentParser recursiveDescentParser;
+ const QStringList inputFileNames = parser.values(inputFileOption);
+ for (const auto &fileName : inputFileNames)
+ success &= readBsdFile(recursiveDescentParser, fileName, false);
+ const QStringList dependencyInputFileNames = parser.values(inputDependencyFileOption);
+ for (const auto &fileName : dependencyInputFileNames)
+ success &= readBsdFile(recursiveDescentParser, fileName, true);
+ if (success) {
+ const auto outputPath = parser.value(outputDirectoryPathOption);
+ if (generateBsdFiles(recursiveDescentParser,
+ outputPath,
+ dataPrefix,
+ outputFileHeader)) {
+ qInfo() << "Info: All types were successfully generated";
+ return EXIT_SUCCESS;
+ }
+ }
+ return EXIT_FAILURE;
+}
diff --git a/tools/datatypecodegenerator/mappingfilegenerator.cpp b/tools/datatypecodegenerator/mappingfilegenerator.cpp
new file mode 100644
index 0000000..de9e2d7
--- /dev/null
+++ b/tools/datatypecodegenerator/mappingfilegenerator.cpp
@@ -0,0 +1,636 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+#include "enumeratedtype.h"
+#include "field.h"
+#include "mappingfilegenerator.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+
+#include <QtCore/qdir.h>
+#include <QtCore/qfile.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+
+MappingFileGenerator::MappingFileGenerator(const QList<XmlElement *> &generateMapping,
+ const QString &path,
+ const QString &prefix,
+ const QString &header)
+ : m_generateMapping(generateMapping)
+ , m_path(path)
+ , m_prefix(prefix)
+ , m_header(header)
+{
+ for (const auto &type : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(type);
+ if (structuredType) {
+ for (const auto &possibleField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleField->name() == field->lengthField()
+ && !m_lengthFields.contains(possibleField))
+ m_lengthFields.push_back(possibleField);
+
+ if (!m_switchFields.contains(possibleField)
+ && possibleField->name() == field->switchField())
+ m_switchFields.push_back(possibleField);
+ }
+ }
+ }
+ }
+}
+
+void MappingFileGenerator::addGenerateMapping(
+ const QList<XmlElement *> &generateMapping)
+{
+ for (const auto &mapping : generateMapping) {
+ if (!m_generateMapping.contains(mapping))
+ m_generateMapping.push_back(mapping);
+ }
+ for (const auto &mapping : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType) {
+ for (const auto &possibleField : structuredType->fields()) {
+ for (const auto &field : structuredType->fields()) {
+ if (possibleField->name() == field->lengthField()
+ && !m_lengthFields.contains(possibleField))
+ m_lengthFields.push_back(possibleField);
+
+ if (!m_switchFields.contains(possibleField)
+ && possibleField->name() == field->switchField())
+ m_switchFields.push_back(possibleField);
+ }
+ }
+ }
+ }
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::generateMapping()
+{
+ auto error = sortMappings();
+ if (error != NoError)
+ return error;
+
+ if (generateMappingHeader() == NoError)
+ error = generateMappingCpp();
+ else
+ error = UnanbleToWrite;
+ return error;
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::generateMappingHeader()
+{
+ QFile file;
+ const auto fileName = QStringLiteral("%1binarydeencoder%2")
+ .arg(m_prefix.toLower(), StringIdentifier::headerIdentifier);
+ QDir dir(m_path);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnanbleToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+
+ // Print header (if present)
+ if (!m_header.isEmpty())
+ output << m_header << Util::lineBreak(2);
+ output << "#pragma once"
+ << Util::lineBreak(2);
+ QList<QString> includes;
+ for (auto mapping : m_generateMapping) {
+ const auto enumeratedType = dynamic_cast<EnumeratedType *>(mapping);
+ if (enumeratedType) {
+ if (!includes.contains(
+ QLatin1String("#include \"%1enumerations.h\"%2").arg(m_prefix.toLower(), Util::lineBreak())))
+ includes.push_back(
+ QLatin1String("#include \"%1enumerations.h\"%2").arg(m_prefix.toLower(), Util::lineBreak()));
+ } else {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType)
+ if (!includes.contains(
+ QLatin1String("#include \"%1%2.h\"%3")
+ .arg(m_prefix.toLower(), structuredType->name().toLower(), Util::lineBreak())))
+ includes.push_back(
+ QLatin1String("#include \"%1%2.h\"%3")
+ .arg(m_prefix.toLower(), structuredType->name().toLower(), Util::lineBreak()));
+ }
+ }
+
+ includes.sort();
+ for (const auto &include : includes) {
+ output << include;
+ }
+ output << Util::lineBreak();
+ output << "#include <QtOpcUa/QOpcUaBinaryDataEncoding>"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+
+ output << "class " << m_prefix << "BinaryDeEncoder"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "public:"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << "BinaryDeEncoder(QByteArray *buffer);"
+ << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << "BinaryDeEncoder(QOpcUaExtensionObject &object);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "T decode(bool &success);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QList<T> decodeArray(bool &success);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "bool encode(const T &src);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "template <typename T>"
+ << Util::lineBreak();
+ output << Util::indent(1) << "bool encodeArray(const QList<T> &src);"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+ output << Util::indent(1) << "QOpcUaBinaryDataEncoding &binaryDataEncoding();"
+ << Util::lineBreak();
+ output << Util::lineBreak();
+
+ output << "private:"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QScopedPointer<QOpcUaBinaryDataEncoding> m_binaryDataEncoding;"
+ << Util::lineBreak();
+
+ output << "};"
+ << "\n\n";
+ generateDeEncodingArray(output);
+ generateDeEncoding(output);
+ return NoError;
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::generateMappingCpp()
+{
+ QFile file;
+ QDir dir(m_path);
+ const auto fileName = QStringLiteral("%1binarydeencoder%2")
+ .arg(m_prefix.toLower(), StringIdentifier::cppIdentifier);
+ if (!dir.exists(m_path))
+ if (!dir.mkpath(m_path))
+ return UnanbleToWrite;
+ file.setFileName(dir.absoluteFilePath(fileName));
+
+ file.open(QIODevice::WriteOnly | QIODevice::Text);
+ QTextStream output(&file);
+
+ // Print header (if present)
+ if (!m_header.isEmpty())
+ output << m_header << "\n\n";
+
+ output << "#include \"" << m_prefix.toLower() << "binarydeencoder.h\"" << Util::lineBreak();
+ output << Util::lineBreak();
+ output << m_prefix << "BinaryDeEncoder::" << m_prefix << "BinaryDeEncoder (QByteArray *buffer)"
+ << Util::lineBreak();
+ output << Util::indent(1) << ": m_binaryDataEncoding(new QOpcUaBinaryDataEncoding(buffer))"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+ output << m_prefix << "BinaryDeEncoder::" << m_prefix
+ << "BinaryDeEncoder "
+ "(QOpcUaExtensionObject &object) "
+ << Util::lineBreak();
+ output << Util::indent(1) << ": m_binaryDataEncoding(new "
+ "QOpcUaBinaryDataEncoding(&object.encodedBodyRef()))"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak();
+ output << "QOpcUaBinaryDataEncoding &" << m_prefix << "BinaryDeEncoder::binaryDataEncoding()"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return *m_binaryDataEncoding.data();"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak();
+ return NoError;
+}
+
+void MappingFileGenerator::generateFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "temp.set" << field->name() << "(";
+ generateScalarOrArrayDecoding(output, structuredType, field);
+ output << ");" << Util::lineBreak();
+ output << Util::indent(level) << "if (!success)" << Util::lineBreak();
+ output << Util::indent(level + 1) << "return {};" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateScalarOrArrayDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field)
+{
+ Q_UNUSED(structuredType);
+ const auto builtinType = StringIdentifier::typeNameDataTypeConverter.constFind(field->typeName());
+ if (builtinType != StringIdentifier::typeNameDataTypeConverter.constEnd()) {
+ output << "m_binaryDataEncoding->decode" << (field->lengthField().isEmpty() ? "" : "Array") << "<" << builtinType.value() << ">(success)";
+ return;
+ }
+
+ bool isPrecoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ output << "m_binaryDataEncoding->decode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<";
+ if (!precodedType.deEncoderName().isEmpty())
+ output << precodedType.deEncoderName() << ", QOpcUa::Types::NodeId";
+ else
+ output << precodedType.className();
+ output << ">(success)";
+ isPrecoded = true;
+ break;
+ }
+ }
+ if (!isPrecoded) {
+ output << "decode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<" << m_prefix << (field->isEnum() ? "::" : "") << Util::removeNamespace(field->typeName()) << ">(success)";
+ }
+}
+
+void MappingFileGenerator::generateOptionalFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ int index = -1;
+ QSet<QString> usedSwitchFields;
+ for (const auto &current : structuredType->fields()) {
+ if (!current->switchField().isEmpty() && !usedSwitchFields.contains(current->switchField())) {
+ index++;
+ usedSwitchFields.insert(current->switchField());
+ }
+
+ if (current == field)
+ break;
+ }
+
+ output << Util::indent(level) << "if (decodingMask & " << QStringLiteral("(1 << %1)").arg(index) << ") {" << Util::lineBreak();
+ generateFieldDecoding(output, structuredType, field, level + 1);
+ output << Util::indent(level + 1) << "temp.set" << field->switchField() << "(true);" << Util::lineBreak();
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateUnionFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << QStringLiteral("if (switchField == %1) {").arg(field->switchValue()) << Util::lineBreak();
+ generateFieldDecoding(output, structuredType, field, level + 1);
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "if (!";
+ generateScalarOrArrayEncoding(output, structuredType, field);
+ output << ")" << Util::lineBreak();
+ output << Util::indent(level + 1) << "return false;" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateScalarOrArrayEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field)
+{
+ Q_UNUSED(structuredType);
+ const auto builtinType = StringIdentifier::typeNameDataTypeConverter.constFind(field->typeName());
+ if (builtinType != StringIdentifier::typeNameDataTypeConverter.constEnd()) {
+ output << "m_binaryDataEncoding->encode" << (field->lengthField().isEmpty() ? "" : "Array") << "<" << builtinType.value() << ">(src." << field->lowerFirstName() << "())";
+ return;
+ }
+
+ bool isPrecoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ output << "m_binaryDataEncoding->encode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<";
+ if (!precodedType.deEncoderName().isEmpty())
+ output << precodedType.deEncoderName() << ", QOpcUa::Types::NodeId";
+ else
+ output << precodedType.className();
+ output << ">(src." << field->lowerFirstName() << "())";
+ isPrecoded = true;
+ break;
+ }
+ }
+ if (!isPrecoded) {
+ output << "encode";
+ if (!field->lengthField().isEmpty())
+ output << "Array";
+ output << "<" << m_prefix << (field->isEnum() ? "::" : "") << Util::removeNamespace(field->typeName()) << ">(src." << field->lowerFirstName() << "())";
+ }
+}
+
+void MappingFileGenerator::generateOptionalFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "if (src." << Util::lowerFirstLetter(field->switchField()) << "()) {" << Util::lineBreak();
+ generateFieldEncoding(output, structuredType, field, level + 1);
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateUnionFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level)
+{
+ output << Util::indent(level) << "if (static_cast<quint32>(src." << structuredType->fields().constFirst()->lowerFirstName() << "()) == " << field->switchValue() << ") {" << Util::lineBreak();
+ generateFieldEncoding(output, structuredType, field, level + 1);
+ output << Util::indent(level) << "}" << Util::lineBreak();
+}
+
+void MappingFileGenerator::generateDeEncodingArray(QTextStream &output)
+{
+ output << "template<typename T>"
+ << Util::lineBreak();
+ output << "inline QList<T> " << m_prefix << "BinaryDeEncoder::decodeArray(bool &success)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "QList<T> temp;"
+ << Util::lineBreak(2);
+ output << Util::indent(1) << "qint32 size = m_binaryDataEncoding->decode<qint32>(success);"
+ << Util::lineBreak();
+ output << Util::indent(1) << "if (!success)"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return {};";
+ output << Util::lineBreak(2);
+ output << Util::indent(1) << "for (int i = 0; i < size; ++i) {"
+ << Util::lineBreak();
+ output << Util::indent(2) << "temp.push_back(decode<T>(success));"
+ << Util::lineBreak();
+ output << Util::indent(2) << "if (!success)"
+ << Util::lineBreak();
+ output << Util::indent(3) << "return {};"
+ << Util::lineBreak();
+ output << Util::indent(1) << "}"
+ << Util::lineBreak(2);
+ output << Util::indent(1) << "return temp;"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+ output << "template<typename T>"
+ << Util::lineBreak();
+ output << "inline bool " << m_prefix << "BinaryDeEncoder::encodeArray(const QList<T> &src)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "if (src.size() > (std::numeric_limits<qint32>::max()))"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return false;"
+ << Util::lineBreak(2);
+ output << Util::indent(1) << "if (!m_binaryDataEncoding->encode<qint32>(src.size()))"
+ << Util::lineBreak();
+ output << Util::indent(2) << "return false;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "for (const auto &element : src) {"
+ << Util::lineBreak();
+ output << Util::indent(2) << "if (!encode<T>(element))"
+ << Util::lineBreak();
+ output << Util::indent(3) << "return false;"
+ << Util::lineBreak();
+ output << Util::indent(1) << "}"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return true;"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateDeEncoding(QTextStream &output)
+{
+ for (const auto &mapping : m_generateMapping) {
+ const auto enumeratedType = dynamic_cast<EnumeratedType *>(mapping);
+ if (enumeratedType) {
+ generateDecodingEnumeratedType(output, enumeratedType);
+ generateEncodingEnumeratedType(output, enumeratedType);
+ } else {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType) {
+ generateDecodingStructuredType(output, structuredType);
+ generateEncodingStructuredType(output, structuredType);
+ }
+ }
+ }
+}
+
+void MappingFileGenerator::generateDecodingEnumeratedType(QTextStream &output,
+ const EnumeratedType *enumeratedType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline " << m_prefix << "::" << enumeratedType->name() << " " << m_prefix
+ << "BinaryDeEncoder::decode(bool &success)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return static_cast<" << m_prefix << "::" << enumeratedType->name() << ">("
+ "m_binaryDataEncoding->decode<qint32>(success));"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateEncodingEnumeratedType(QTextStream &output,
+ const EnumeratedType *enumeratedType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline bool " << m_prefix << "BinaryDeEncoder"
+ << "::encode<" << m_prefix << "::" << enumeratedType->name() << ">(const " << m_prefix
+ << "::" << enumeratedType->name() << " &src)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+ output << Util::indent(1) << "return m_binaryDataEncoding->encode<qint32>(static_cast<qint32>(src));"
+ << Util::lineBreak();
+ output << "}"
+ << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateDecodingStructuredType(QTextStream &output,
+ const StructuredType *structuredType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline " << m_prefix << structuredType->name() << " " << m_prefix
+ << "BinaryDeEncoder::decode<" << m_prefix << structuredType->name() << ">(bool &success)" << Util::lineBreak()
+ << "{" << Util::lineBreak();
+ output << Util::indent(1) << m_prefix << structuredType->name() << " temp;"
+ << Util::lineBreak(2);
+
+ if (structuredType->containsBitMask()) {
+ output << Util::indent(1) <<"const auto decodingMask = "
+ << "m_binaryDataEncoding->decode<quint32>(success);" << Util::lineBreak()
+ << Util::indent(1) << "if (!success)" << Util::lineBreak()
+ << Util::indent(2) << "return {};" << Util::lineBreak(2);
+ } else if (structuredType->hasUnion()) {
+ output << Util::indent(1) <<"const auto switchField = "
+ << "m_binaryDataEncoding->decode<quint32>(success);" << Util::lineBreak()
+ << Util::indent(1) << "if (!success)" << Util::lineBreak()
+ << Util::indent(2) << "return {};" << Util::lineBreak(2);
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (field->isInStructuredTypeBitMask())
+ continue;
+
+ if (m_lengthFields.contains(field))
+ continue;
+
+ // The switch field is not a member of the generated data class
+ if (structuredType->hasUnion() && field == structuredType->fields().constFirst())
+ continue;
+
+ if (!field->switchField().isEmpty() && !field->isUnion())
+ generateOptionalFieldDecoding(output, structuredType, field);
+ else if (structuredType->hasUnion())
+ generateUnionFieldDecoding(output, structuredType, field);
+ else
+ generateFieldDecoding(output, structuredType, field);
+
+ output << Util::lineBreak();
+ }
+
+ output << Util::indent(1) << "return temp;" << Util::lineBreak();
+ output << "}" << Util::lineBreak(2);
+}
+
+void MappingFileGenerator::generateEncodingStructuredType(QTextStream &output,
+ const StructuredType *structuredType)
+{
+ output << "template<>"
+ << Util::lineBreak();
+ output << "inline bool " << m_prefix << "BinaryDeEncoder"
+ << "::encode<" << m_prefix << structuredType->name() << ">(const " << m_prefix
+ << structuredType->name() << " &src)"
+ << Util::lineBreak();
+ output << "{"
+ << Util::lineBreak();
+
+ // Create encoding mask for bitmask
+ if (structuredType->hasSwitchfield() && !structuredType->hasUnion()) {
+ output << Util::indent(1) << "quint32 encodingMask = 0;" << Util::lineBreak();
+ int index = 0;
+ for (const auto &field : structuredType->fields()) {
+ if (!m_switchFields.contains(field))
+ continue;
+ output << Util::indent(1) << "encodingMask |= ((src." << field->lowerFirstName() << "() ? 1 : 0) << " << index++ << ");" << Util::lineBreak();
+ }
+ output << Util::lineBreak();
+ output << Util::indent(1) << "if (!m_binaryDataEncoding->encode<quint32>(encodingMask))" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ } else if (structuredType->hasUnion()) {
+ output << Util::indent(1) << "if (!m_binaryDataEncoding->encode<quint32>(static_cast<quint32>(src." << structuredType->fields().constFirst()->lowerFirstName() << "())))" << Util::lineBreak();
+ output << Util::indent(2) << "return false;" << Util::lineBreak(2);
+ }
+
+ for (const auto &field : structuredType->fields()) {
+ if (m_lengthFields.contains(field))
+ continue;
+
+ if (m_switchFields.contains(field))
+ continue;
+
+ // Ignore reserved fields
+ if (field->isInStructuredTypeBitMask())
+ continue;
+
+ if (structuredType->hasUnion())
+ generateUnionFieldEncoding(output, structuredType, field);
+ else if (!field->switchField().isEmpty() && !field->isUnion())
+ generateOptionalFieldEncoding(output, structuredType, field);
+ else
+ generateFieldEncoding(output, structuredType, field);
+
+ output << Util::lineBreak();
+ }
+
+ output << Util::indent(1) << "return true;" << Util::lineBreak();
+ output << "}" << Util::lineBreak(2);
+}
+
+MappingFileGenerator::MappingError MappingFileGenerator::sortMappings()
+{
+ QList<XmlElement *> baseType;
+ QList<XmlElement *> extendedType;
+ for (const auto &mapping : m_generateMapping) {
+ const auto structuredType = dynamic_cast<StructuredType *>(mapping);
+ if (structuredType) {
+ bool isBaseType = true;
+ for (const auto &field : structuredType->fields()) {
+ bool isPreCoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ isPreCoded = true;
+ break;
+ }
+ }
+ if (!isPreCoded)
+ if (!StringIdentifier::typeNameDataTypeConverter.contains(field->typeName()))
+ if (Util::removeNamespace(field->typeName()) != structuredType->name())
+ isBaseType = false;
+ }
+ if (isBaseType)
+ baseType.push_back(structuredType);
+ else
+ extendedType.push_back(structuredType);
+ } else {
+ baseType.push_front(mapping);
+ }
+ }
+ m_generateMapping = baseType;
+
+ while (!extendedType.isEmpty()) {
+ QList<XmlElement *> tmpExtendedType = extendedType;
+ for (int i = 0; i < extendedType.size(); i++) {
+ const auto structuredType = dynamic_cast<StructuredType *>(extendedType.at(i));
+ if (structuredType) {
+ bool independentExtended = true;
+ for (const auto &field : structuredType->fields()) {
+ bool isPreCoded = false;
+ for (const auto &precodedType : StringIdentifier::opcUaPrecodedTypes) {
+ if (precodedType.contains(Util::removeNamespace(field->typeName()))) {
+ isPreCoded = true;
+ break;
+ }
+ }
+ bool basicdependecy = false;
+ if (StringIdentifier::typeNameDataTypeConverter.contains(field->typeName()))
+ basicdependecy = true;
+ else if (!isPreCoded
+ && Util::removeNamespace(field->typeName()) != structuredType->name()) {
+ for (const auto &type : m_generateMapping) {
+ if (type->name() == Util::removeNamespace(field->typeName())) {
+ basicdependecy = true;
+ }
+ }
+ } else if (!isPreCoded
+ && Util::removeNamespace(field->typeName()) == structuredType->name()) {
+ basicdependecy = true;
+ } else if (isPreCoded)
+ basicdependecy = true;
+ independentExtended &= basicdependecy;
+ }
+
+ if (independentExtended) {
+ m_generateMapping.append(structuredType);
+ extendedType.removeAt(extendedType.indexOf(structuredType));
+ }
+ }
+ }
+ if (extendedType == tmpExtendedType) {
+ return MappingFileGenerator::MappingError::UnableToResolve;
+ }
+ }
+ return MappingFileGenerator::MappingError::NoError;
+}
diff --git a/tools/datatypecodegenerator/mappingfilegenerator.h b/tools/datatypecodegenerator/mappingfilegenerator.h
new file mode 100644
index 0000000..923e69b
--- /dev/null
+++ b/tools/datatypecodegenerator/mappingfilegenerator.h
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qlist.h>
+#include <QtCore/qtextstream.h>
+
+#include <cmath>
+#include <iostream>
+
+class EnumeratedType;
+class Field;
+class StructuredType;
+class XmlElement;
+
+class MappingFileGenerator
+{
+public:
+ enum MappingError { NoError, UnanbleToWrite, UnableToResolve };
+
+ MappingFileGenerator(const QList<XmlElement *> &generateMapping,
+ const QString &path,
+ const QString &prefix,
+ const QString &header);
+ ~MappingFileGenerator() = default;
+
+ void addGenerateMapping(const QList<XmlElement *> &generateMapping);
+
+ MappingError generateMapping();
+
+private:
+ QList<Field *> m_lengthFields;
+ QList<Field *> m_switchFields;
+ QList<XmlElement *> m_generateMapping;
+ QString m_path;
+ QString m_prefix;
+ QString m_header;
+
+ MappingError generateMappingHeader();
+ MappingError generateMappingCpp();
+
+ void generateFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateScalarOrArrayDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field);
+ void generateOptionalFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateUnionFieldDecoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+
+ void generateFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateScalarOrArrayEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field);
+ void generateOptionalFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+ void generateUnionFieldEncoding(QTextStream &output, const StructuredType *structuredType, const Field *field, int level = 1);
+
+ void generateDeEncodingArray(QTextStream &output);
+
+ void generateDeEncoding(QTextStream &output);
+
+ void generateDecodingEnumeratedType(QTextStream &output, const EnumeratedType *enumeratedType);
+ void generateEncodingEnumeratedType(QTextStream &output, const EnumeratedType *enumeratedType);
+
+ void generateDecodingStructuredType(QTextStream &output, const StructuredType *structuredType);
+ void generateEncodingStructuredType(QTextStream &output, const StructuredType *structuredType);
+
+ MappingFileGenerator::MappingError sortMappings();
+};
diff --git a/tools/datatypecodegenerator/recursivedescentparser.cpp b/tools/datatypecodegenerator/recursivedescentparser.cpp
new file mode 100644
index 0000000..92e8adf
--- /dev/null
+++ b/tools/datatypecodegenerator/recursivedescentparser.cpp
@@ -0,0 +1,389 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+#include "datatypefilewriter.h"
+#include "dependencydatatypevalidator.h"
+#include "enumeratedtype.h"
+#include "enumeratedvalue.h"
+#include "field.h"
+#include "import.h"
+#include "mappingfilegenerator.h"
+#include "recursivedescentparser.h"
+#include "stringidentifier.h"
+#include "structuredtype.h"
+#include "typedictionary.h"
+
+#include <QtCore/qfile.h>
+
+RecursiveDescentParser::~RecursiveDescentParser()
+{
+ qDeleteAll(m_mapTypeDictionary);
+}
+
+void RecursiveDescentParser::printInOrder() const
+{
+ for (const auto &entry : m_mapTypeDictionary) {
+ entry->print();
+ }
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedType(
+ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
+{
+ EnumeratedType *enumeratedType = new EnumeratedType(
+ xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::lengthInBitsIdentifier).toInt());
+ typeDictionary->addType(enumeratedType);
+ xmlStreamReader.readNext();
+ ParsingError error = NoError;
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::enumeratedValueIdentifier)
+ error = parseEnumeratedValue(xmlStreamReader, enumeratedType);
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidEnumeratedTypeEntry;
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseEnumeratedValue(
+ QXmlStreamReader &xmlStreamReader, EnumeratedType *enumeratedType)
+{
+ const auto name = permittedName(xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString());
+
+ if (name.isEmpty())
+ return ParsingError::InvalidEnumeratedValueEntry;
+
+ EnumeratedValue *enumeratedValue = new EnumeratedValue(
+ name, xmlStreamReader.attributes().value(StringIdentifier::valueIdentifier).toInt());
+ enumeratedType->addValue(enumeratedValue);
+ ParsingError error = NoError;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ } else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidEnumeratedValueEntry;
+ }
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseField(
+ QXmlStreamReader &xmlStreamReader, StructuredType *structuredType)
+{
+ const auto name = permittedName(xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString());
+
+ if (name.isEmpty())
+ return ParsingError::InvalidFieldEntry;
+
+ Field *field = new Field(
+ name,
+ xmlStreamReader.attributes().value(StringIdentifier::typeNameIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::lengthFieldIdentifier).toString());
+ if (!field->lengthField().isEmpty())
+ field->setNeedContainer(true);
+
+ if (field->typeName().endsWith(QStringLiteral(":%1").arg(structuredType->name())))
+ field->setRecursive(true);
+
+ if (!xmlStreamReader.attributes().value(StringIdentifier::lengthIdentifier).toString().isEmpty()) {
+ field->setLength(xmlStreamReader.attributes().value(StringIdentifier::lengthIdentifier).toUInt());
+ field->setIsInStructuredTypeBitMask(true);
+ }
+ if (!xmlStreamReader.attributes()
+ .value(StringIdentifier::switchFieldIdentifier)
+ .toString()
+ .isEmpty()) {
+ structuredType->setHasSwitchfield(true);
+ field->setSwitchField(
+ xmlStreamReader.attributes().value(StringIdentifier::switchFieldIdentifier).toString());
+ }
+ if (!xmlStreamReader.attributes()
+ .value(StringIdentifier::switchValueIdentifier)
+ .toString()
+ .isEmpty()) {
+ structuredType->setHasUnion(true);
+ field->setIsUnion(true);
+ field->setSwitchValue(
+ xmlStreamReader.attributes().value(StringIdentifier::switchValueIdentifier).toInt());
+ }
+ if (field->typeName() == StringIdentifier::opcBitIdentifier)
+ structuredType->setContainsBitMask(true);
+ if (field->typeName().contains(StringIdentifier::bitIdentifier)
+ && (field->name().contains(StringIdentifier::specifiedIdentifier)
+ || field->name().contains(StringIdentifier::reservedIdentifier))
+ && structuredType->containsBitMask())
+ field->setIsInStructuredTypeBitMask(true);
+ structuredType->addField(field);
+ ParsingError error = NoError;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ } else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidFieldEntry;
+ }
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseFile(
+ const QString &fileName, const bool &dependencyTypeDictionary)
+{
+ ParsingError error = NoError;
+ QFile xmlFile(fileName);
+ if (!xmlFile.open(QIODevice::ReadOnly))
+ return InvalidFileName;
+ QXmlStreamReader xmlStreamReader;
+ xmlStreamReader.setDevice((&xmlFile));
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::typeDictionaryIdentifier)
+ error = parseTypeDictionary(xmlStreamReader, dependencyTypeDictionary);
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidTypeDictionaryEntry;
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+ xmlFile.close();
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseImport(
+ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
+{
+ Import *import = new Import(
+ xmlStreamReader.attributes().value(StringIdentifier::nameSpaceIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::locationIdentifier).toString());
+ typeDictionary->addType(import);
+ xmlStreamReader.readNext();
+ ParsingError error = NoError;
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ } else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidImportEntry;
+ }
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseStructuredType(
+ QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary)
+{
+ StructuredType *structuredType = new StructuredType(
+ xmlStreamReader.attributes().value(StringIdentifier::nameIdentifier).toString(),
+ xmlStreamReader.attributes().value(StringIdentifier::baseTypeIdentifier).toString());
+ if (StringIdentifier::buildInTypesWithBitMask.contains(structuredType->name())) {
+ structuredType->setIsBuiltInType(true);
+ structuredType->setContainsBitMask(true);
+ }
+ structuredType->setTargetNamespace(typeDictionary->targetNamespace());
+ typeDictionary->addType(structuredType);
+ xmlStreamReader.readNext();
+ ParsingError error = NoError;
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+
+ for (const auto &field : structuredType->fields()) {
+ if (field->recursive()) {
+ structuredType->setRecursive(true);
+ break;
+ }
+ }
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::fieldIdentifier)
+ error = parseField(xmlStreamReader, structuredType);
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidStructuredTypeEntry;
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::parseTypeDictionary(
+ QXmlStreamReader &xmlStreamReader, const bool &dependencyTypeDictionary)
+{
+ QMap<QString, QString> namespaces;
+ for (int i = 0; i < xmlStreamReader.namespaceDeclarations().size(); i++)
+ namespaces.insert(xmlStreamReader.namespaceDeclarations().at(i).prefix().toString(),
+ xmlStreamReader.namespaceDeclarations().at(i).namespaceUri().toString());
+ m_mapTypeDictionary.insert(
+ xmlStreamReader.attributes().value(StringIdentifier::targetNamespaceIdentifier).toString(),
+ new TypeDictionary(dependencyTypeDictionary,
+ xmlStreamReader.attributes()
+ .value(StringIdentifier::defaultByteOrderIdentifier)
+ .toString(),
+ xmlStreamReader.attributes()
+ .value(StringIdentifier::targetNamespaceIdentifier)
+ .toString(),
+ namespaces));
+ QString targetNamespace
+ = xmlStreamReader.attributes().value(StringIdentifier::targetNamespaceIdentifier).toString();
+ ParsingError error = NoError;
+ if (!dependencyTypeDictionary && targetNamespace == StringIdentifier::namespaceZeroIdentifier)
+ return CannotFullyGenerateNamespaceZero;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::enumeratedTypeIdentifier)
+ error = parseEnumeratedType(xmlStreamReader,
+ m_mapTypeDictionary.value(targetNamespace));
+ else {
+ if (xmlStreamReader.name() == StringIdentifier::structuredTypeIdentifier)
+ error = parseStructuredType(xmlStreamReader,
+ m_mapTypeDictionary.value(targetNamespace));
+ else {
+ if (xmlStreamReader.name() == StringIdentifier::importIdentifier)
+ error = parseImport(xmlStreamReader,
+ m_mapTypeDictionary.value(targetNamespace));
+ else {
+ if (isKnownElement(xmlStreamReader))
+ error = skipKnownElement(xmlStreamReader);
+ else
+ error = InvalidTypeDictionaryEntry;
+ }
+ }
+ }
+ } else
+ xmlStreamReader.readNext();
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::skipKnownElement(
+ QXmlStreamReader &xmlStreamReader)
+{
+ ParsingError error = NoError;
+ xmlStreamReader.readNext();
+ while (!xmlStreamReader.atEnd() && error == NoError) {
+ if (xmlStreamReader.isEndElement()) {
+ xmlStreamReader.readNext();
+ return error;
+ }
+ if (xmlStreamReader.isStartElement()) {
+ if (xmlStreamReader.name() == StringIdentifier::documentationIdentifier
+ || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier)
+ skipKnownElement(xmlStreamReader);
+ else
+ error = UnknownEntry;
+ } else
+ xmlStreamReader.readNext();
+ }
+ return error;
+}
+
+RecursiveDescentParser::ParsingError RecursiveDescentParser::generateInputFiles(
+ const QString &path,
+ const QString &prefix,
+ const QString &header)
+{
+ DependencyDataTypeValidator dependencyDataTypeValidator;
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
+ if (!it.value()->dependencyTypeDictionary())
+ it.value()->accept(&dependencyDataTypeValidator);
+ }
+
+ dependencyDataTypeValidator.setReadResolveDependencies(
+ DependencyDataTypeValidator::ResolveDependencies);
+
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
+ it.value()->accept(&dependencyDataTypeValidator);
+
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it)
+ it.value()->accept(&dependencyDataTypeValidator);
+
+ if (dependencyDataTypeValidator.unresolvedDependencyStringList().isEmpty()) {
+ DataTypeFileWriter filewriter(path,
+ prefix,
+ header);
+ for (auto it = m_mapTypeDictionary.constBegin(); it != m_mapTypeDictionary.constEnd(); ++it) {
+ if (!it.value()->dependencyTypeDictionary())
+ it.value()->accept(&filewriter);
+ };
+ if (filewriter.generateMapping().isEmpty()) {
+ return UnableToWriteFile;
+ }
+ MappingFileGenerator mappingFileGenerator(filewriter.generateMapping(), path, prefix, header);
+ if (filewriter.generateTypes(dependencyDataTypeValidator.resolvedDependencyElementList())
+ != DataTypeFileWriter::GeneratingError::NoError) {
+ return UnableToWriteFile;
+ }
+
+ mappingFileGenerator.addGenerateMapping(dependencyDataTypeValidator.resolvedDependencyElementList());
+
+ if (mappingFileGenerator.generateMapping() != MappingFileGenerator::MappingError::NoError)
+ return UnableToWriteFile;
+ } else {
+ return MissingDependency;
+ }
+
+ return NoError;
+}
+
+bool RecursiveDescentParser::isKnownElement(const QXmlStreamReader &xmlStreamReader)
+{
+ return xmlStreamReader.name() == StringIdentifier::documentationIdentifier
+ || xmlStreamReader.name() == StringIdentifier::opaqueTypeIdentifier;
+}
+
+QString RecursiveDescentParser::permittedName(const QString &name) const
+{
+ if (name.isEmpty())
+ return {};
+
+ auto result = name;
+
+ if (StringIdentifier::illegalNames.contains(name)) {
+ result = Util::lowerFirstLetter(name);
+ result = QStringLiteral("_%1").arg(name);
+ }
+
+ if (result.contains(QStringLiteral(" ")))
+ result.replace(QStringLiteral(" "), QStringLiteral("_"));
+ if (name.contains(QStringLiteral("-")))
+ result.replace(QStringLiteral("-"), QStringLiteral("_"));
+
+ return result;
+}
diff --git a/tools/datatypecodegenerator/recursivedescentparser.h b/tools/datatypecodegenerator/recursivedescentparser.h
new file mode 100644
index 0000000..3e540f4
--- /dev/null
+++ b/tools/datatypecodegenerator/recursivedescentparser.h
@@ -0,0 +1,66 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qmap.h>
+#include <QtCore/qxmlstream.h>
+
+class EnumeratedType;
+class StructuredType;
+class TypeDictionary;
+
+class RecursiveDescentParser
+{
+ Q_GADGET
+public:
+ enum ParsingError {
+ NoError,
+ InvalidFileName,
+ InvalidTypeDictionaryEntry,
+ InvalidImportEntry,
+ InvalidEnumeratedTypeEntry,
+ InvalidStructuredTypeEntry,
+ InvalidEnumeratedValueEntry,
+ InvalidFieldEntry,
+ UnknownEntry,
+ MissingDependency,
+ CannotFullyGenerateNamespaceZero,
+ UnableToWriteFile,
+ UnableToResolveDependency
+ };
+ Q_ENUM(ParsingError)
+
+ RecursiveDescentParser() = default;
+ ~RecursiveDescentParser();
+
+ void printInOrder() const;
+ ParsingError parseEnumeratedType(QXmlStreamReader &xmlStreamReader,
+ TypeDictionary *typeDictionary);
+
+ ParsingError parseEnumeratedValue(QXmlStreamReader &xmlStreamReader,
+ EnumeratedType *enumeratedType);
+ ParsingError parseField(QXmlStreamReader &xmlStreamReader, StructuredType *structuredType);
+
+ ParsingError parseFile(const QString &fileName, const bool &dependencyTypeDictionary);
+ ParsingError parseImport(QXmlStreamReader &xmlStreamReader, TypeDictionary *typeDictionary);
+
+ ParsingError parseStructuredType(QXmlStreamReader &xmlStreamReader,
+ TypeDictionary *typeDictionary);
+ ParsingError parseTypeDictionary(QXmlStreamReader &xmlStreamReader,
+ const bool &dependencyTypeDictionary);
+
+ ParsingError skipKnownElement(QXmlStreamReader &xmlStreamReader);
+ ParsingError generateInputFiles(const QString &path,
+ const QString &prefix,
+ const QString &header);
+
+ ParsingError dependencyCheck();
+ bool isKnownElement(const QXmlStreamReader &xmlStreamReader);
+
+ QString permittedName(const QString &name) const;
+
+private:
+ QString m_fileName;
+ QMap<QString, TypeDictionary *> m_mapTypeDictionary;
+};
diff --git a/tools/datatypecodegenerator/stringidentifier.cpp b/tools/datatypecodegenerator/stringidentifier.cpp
new file mode 100644
index 0000000..1a2b9bb
--- /dev/null
+++ b/tools/datatypecodegenerator/stringidentifier.cpp
@@ -0,0 +1,127 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "stringidentifier.h"
+
+const QString StringIdentifier::baseTypeIdentifier = "BaseType";
+const QString StringIdentifier::binaryTypeIdentifier = "opc";
+const QString StringIdentifier::bitIdentifier = "Bit";
+const QString StringIdentifier::booleanIdentifier = "Boolean";
+const QString StringIdentifier::byteIdentifier = "Byte";
+const QString StringIdentifier::sbyteIdentifier = "SByte";
+const QString StringIdentifier::byteStringIdentifier = "ByteString";
+const QString StringIdentifier::charArrayIdentifier = "CharArray";
+const QString StringIdentifier::cppIdentifier = ".cpp";
+const QString StringIdentifier::datetimeIdentifier = "DateTime";
+const QString StringIdentifier::defaultByteOrderIdentifier = "DefaultByteOrder";
+const QString StringIdentifier::documentationIdentifier = "Documentation";
+const QString StringIdentifier::doubleIdentifier = "Double";
+const QString StringIdentifier::enumeratedTypeIdentifier = "EnumeratedType";
+const QString StringIdentifier::enumeratedValueIdentifier = "EnumeratedValue";
+const QString StringIdentifier::fieldIdentifier = "Field";
+const QString StringIdentifier::floatIdentifier = "Float";
+const QString StringIdentifier::guidIdentifier = "Guid";
+const QString StringIdentifier::headerIdentifier = ".h";
+const QString StringIdentifier::importIdentifier = "Import";
+const QString StringIdentifier::integerIdentifier = "Int";
+const QString StringIdentifier::lengthIdentifier = "Length";
+const QString StringIdentifier::lengthFieldIdentifier = "LengthField";
+const QString StringIdentifier::lengthInBitsIdentifier = "LengthInBits";
+const QString StringIdentifier::locationIdentifier = "Location";
+const QString StringIdentifier::mainIdentifier = "main.cpp";
+const QString StringIdentifier::makelistIdentifier = "CMakeLists.txt";
+const QString StringIdentifier::nameIdentifier = "Name";
+const QString StringIdentifier::nameSpaceIdentifier = "Namespace";
+const QString StringIdentifier::namespaceZeroIdentifier = "http://opcfoundation.org/UA/";
+const QString StringIdentifier::opaqueTypeIdentifier = "OpaqueType";
+const QString StringIdentifier::projectIdentifier = "cmakeqt6";
+const QString StringIdentifier::qByteArrayIdentifier = "QByteArray";
+const QString StringIdentifier::qStringIdentifier = "QString";
+const QString StringIdentifier::uaStatusCodeIdentifier = "StatusCode";
+const QString StringIdentifier::reservedIdentifier = "Reserved";
+const QString StringIdentifier::specifiedIdentifier = "Specified";
+const QString StringIdentifier::structuredTypeIdentifier = "StructuredType";
+const QString StringIdentifier::switchFieldIdentifier = "SwitchField";
+const QString StringIdentifier::switchValueIdentifier = "SwitchValue";
+const QString StringIdentifier::targetNamespaceIdentifier = "TargetNamespace";
+const QString StringIdentifier::typeDictionaryIdentifier = "TypeDictionary";
+const QString StringIdentifier::typeNameIdentifier = "TypeName";
+const QString StringIdentifier::valueIdentifier = "Value";
+const QString StringIdentifier::xmlElementIdentifier = "XmlElement";
+const QString StringIdentifier::opcBitIdentifier = "opc:Bit";
+
+const QList<StringIdentifier::OpcUaPrecodedType> StringIdentifier::opcUaPrecodedTypes{
+ OpcUaPrecodedType("ApplicationRecordDataType", "QOpcUaApplicationRecordDataType",
+ "QOpcUaApplicationRecordDataType"),
+ OpcUaPrecodedType("Argument", "QOpcUaArgument", "QOpcUaArgument"),
+ OpcUaPrecodedType("AxisInformation", "QOpcUaAxisInformation", "QOpcUaAxisInformation"),
+ OpcUaPrecodedType("ComplexNumber", "QOpcUaComplexNumber", "QOpcUaComplexNumber"),
+ OpcUaPrecodedType("DoubleComplexNumber", "QOpcUaDoubleComplexNumber", "QOpcUaDoubleComplexNumber"),
+ OpcUaPrecodedType("EUInformation", "QOpcUaEUInformation", "QOpcUaEUInformation"),
+ OpcUaPrecodedType("ExpandedNodeId", "QOpcUaExpandedNodeId", "QOpcUaExpandedNodeId"),
+ OpcUaPrecodedType("ExtensionObject", "QOpcUaExtensionObject", "QOpcUaExtensionObject"),
+ OpcUaPrecodedType("LocalizedText", "QOpcUaLocalizedText", "QOpcUaLocalizedText"),
+ OpcUaPrecodedType("NodeId", QString(), "QString", "QString"),
+ OpcUaPrecodedType("QualifiedName", "QOpcUaQualifiedName", "QOpcUaQualifiedName"),
+ OpcUaPrecodedType("Range", "QOpcUaRange", "QOpcUaRange"),
+ OpcUaPrecodedType("StatusCode", "qopcuatype.h", "QOpcUa::UaStatusCode"),
+ OpcUaPrecodedType("XVType", "QOpcUaXValue", "QOpcUaXValue"),
+ OpcUaPrecodedType("DiagnosticInfo", "QOpcUaDiagnosticInfo", "QOpcUaDiagnosticInfo"),
+ OpcUaPrecodedType("StructureField", "QOpcUaStructureField", "QOpcUaStructureField"),
+ OpcUaPrecodedType("StructureDefinition", "QOpcUaStructureDefinition", "QOpcUaStructureDefinition"),
+ OpcUaPrecodedType("EnumField", "QOpcUaEnumField", "QOpcUaEnumField"),
+ OpcUaPrecodedType("EnumDefinition", "QOpcUaEnumDefinition", "QOpcUaEnumDefinition")
+};
+
+const QList<QString> StringIdentifier::buildInTypesWithBitMask = {"DiagnosticInfo",
+ "LocalizedText",
+ "Variant",
+ "DataValue"};
+
+const QMap<QString, QString> StringIdentifier::typeNameDataTypeConverter
+ = {{"opc:Bit", "bool"},
+ {"opc:Boolean", "bool"},
+ {"opc:Byte", "quint8"},
+ {"opc:ByteString", "QByteArray"},
+ {"opc:CharArray", "QString"},
+ {"opc:DateTime", "QDateTime"},
+ {"opc:Double", "double"},
+ {"opc:Float", "float"},
+ {"opc:Int16", "qint16"},
+ {"opc:Int32", "qint32"},
+ {"opc:Int64", "qint64"},
+ {"opc:SByte", "qint8"},
+ {"opc:String", "QString"},
+ {"opc:UInt16", "quint16"},
+ {"opc:UInt32", "quint32"},
+ {"opc:UInt64", "quint64"},
+ {"opc:Guid", "QUuid"},
+ {"ua:XmlElement", "QString"}};
+
+const QList<QString> StringIdentifier::illegalNames
+ = {"Boolean", "boolean", "Int16", "int16", "Float", "float",
+ "Datetime", "datetime", "byteString", "ByteString", "XmlElement", "xmlElement",
+ "byte", "Byte", "SByte", "sByte", "Int32", "int32",
+ "Int64", "int64", "Double", "double", "String", "string"};
+
+const QSet<QString> StringIdentifier::precodedTypesWithDebugOperator = { "LocalizedText", "NodeId", "QualifiedName" };
+
+QString StringIdentifier::OpcUaPrecodedType::name() const
+{
+ return m_name;
+}
+
+QString StringIdentifier::OpcUaPrecodedType::filename() const
+{
+ return m_filename;
+}
+
+QString StringIdentifier::OpcUaPrecodedType::className() const
+{
+ return m_className;
+}
+
+QString StringIdentifier::OpcUaPrecodedType::deEncoderName() const
+{
+ return m_deEncoderName;
+}
diff --git a/tools/datatypecodegenerator/stringidentifier.h b/tools/datatypecodegenerator/stringidentifier.h
new file mode 100644
index 0000000..53dcff4
--- /dev/null
+++ b/tools/datatypecodegenerator/stringidentifier.h
@@ -0,0 +1,101 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qlist.h>
+#include <QtCore/qmap.h>
+#include <QtCore/qset.h>
+#include <QtCore/qstring.h>
+
+class StringIdentifier
+{
+public:
+ static const QString baseTypeIdentifier;
+ static const QString binaryTypeIdentifier;
+ static const QString bitIdentifier;
+ static const QString booleanIdentifier;
+ static const QString byteIdentifier;
+ static const QString sbyteIdentifier;
+ static const QString byteStringIdentifier;
+ static const QString charArrayIdentifier;
+ static const QString cppIdentifier;
+ static const QString datetimeIdentifier;
+ static const QString defaultByteOrderIdentifier;
+ static const QString documentationIdentifier;
+ static const QString doubleIdentifier;
+ static const QString enumeratedTypeIdentifier;
+ static const QString enumeratedValueIdentifier;
+ static const QString fieldIdentifier;
+ static const QString floatIdentifier;
+ static const QString guidIdentifier;
+ static const QString headerIdentifier;
+ static const QString importIdentifier;
+ static const QString integerIdentifier;
+ static const QString lengthIdentifier;
+ static const QString lengthFieldIdentifier;
+ static const QString lengthInBitsIdentifier;
+ static const QString locationIdentifier;
+ static const QString mainIdentifier;
+ static const QString makelistIdentifier;
+ static const QString nameIdentifier;
+ static const QString nameSpaceIdentifier;
+ static const QString namespaceZeroIdentifier;
+ static const QString opaqueTypeIdentifier;
+ static const QString projectIdentifier;
+ static const QString qByteArrayIdentifier;
+ static const QString qStringIdentifier;
+ static const QString uaStatusCodeIdentifier;
+ static const QString reservedIdentifier;
+ static const QString specifiedIdentifier;
+ static const QString structuredTypeIdentifier;
+ static const QString switchFieldIdentifier;
+ static const QString switchValueIdentifier;
+ static const QString targetNamespaceIdentifier;
+ static const QString typeDictionaryIdentifier;
+ static const QString typeNameIdentifier;
+ static const QString valueIdentifier;
+ static const QString xmlElementIdentifier;
+ static const QString opcBitIdentifier;
+
+ class OpcUaPrecodedType
+ {
+ public:
+ OpcUaPrecodedType(const QString &typeName,
+ const QString &fileName,
+ const QString &className,
+ const QString &deEncoderName = QString())
+ : m_name(typeName)
+ , m_filename(fileName)
+ , m_className(className)
+ , m_deEncoderName(deEncoderName)
+ {}
+
+ bool contains(QString name) const
+ {
+ return m_name == name || m_filename == name || m_className == name;
+ }
+
+ QString name() const;
+
+ QString filename() const;
+
+ QString className() const;
+
+ QString deEncoderName() const;
+
+ private:
+ QString m_name;
+ QString m_filename;
+ QString m_className;
+ QString m_deEncoderName;
+ };
+
+ static const QList<OpcUaPrecodedType> opcUaPrecodedTypes;
+ static const QList<QString> buildInTypesWithBitMask;
+ static const QMap<QString, QString> typeNameDataTypeConverter;
+
+ static const QList<QString> illegalNames;
+
+ static const QSet<QString> precodedTypesWithDebugOperator;
+};
diff --git a/tools/datatypecodegenerator/structuredtype.cpp b/tools/datatypecodegenerator/structuredtype.cpp
new file mode 100644
index 0000000..948e9bc
--- /dev/null
+++ b/tools/datatypecodegenerator/structuredtype.cpp
@@ -0,0 +1,118 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "field.h"
+#include "structuredtype.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+StructuredType::StructuredType(const QString &name, const QString &baseType)
+ : XmlElement(name)
+ , m_baseType(baseType)
+{}
+
+StructuredType::~StructuredType()
+{
+ qDeleteAll(m_fields);
+}
+
+QString StructuredType::baseType() const
+{
+ return m_baseType;
+}
+
+void StructuredType::setBaseType(const QString &baseType)
+{
+ m_baseType = baseType;
+}
+
+void StructuredType::addField(Field *field)
+{
+ m_fields.push_back(field);
+}
+
+void StructuredType::print() const
+{
+ XmlElement::print();
+ qDebug() << "BaseType: " << m_baseType;
+ for (int i = 0; i < m_fields.size(); i++)
+ m_fields.at(i)->print();
+}
+
+void StructuredType::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+ for (const auto &field : m_fields)
+ field->accept(visitor);
+}
+
+QList<Field *> StructuredType::fields() const
+{
+ return m_fields;
+}
+
+void StructuredType::setFields(const QList<Field *> &fields)
+{
+ m_fields = fields;
+}
+
+bool StructuredType::containsBitMask() const
+{
+ return m_containsBitMask;
+}
+
+void StructuredType::setContainsBitMask(bool containsBitMask)
+{
+ m_containsBitMask = containsBitMask;
+}
+
+bool StructuredType::isBuiltInType() const
+{
+ return m_isBuiltInType;
+}
+
+void StructuredType::setIsBuiltInType(bool isBuiltInType)
+{
+ m_isBuiltInType = isBuiltInType;
+}
+
+bool StructuredType::hasSwitchfield() const
+{
+ return m_hasSwitchfield;
+}
+
+void StructuredType::setHasSwitchfield(bool hasSwitchfield)
+{
+ m_hasSwitchfield = hasSwitchfield;
+}
+
+bool StructuredType::hasUnion() const
+{
+ return m_hasUnion;
+}
+
+void StructuredType::setHasUnion(bool hasUnion)
+{
+ m_hasUnion = hasUnion;
+}
+
+QString StructuredType::targetNamespace() const
+{
+ return m_targetNamespace;
+}
+
+void StructuredType::setTargetNamespace(const QString &targetNamespace)
+{
+ m_targetNamespace = targetNamespace;
+}
+
+bool StructuredType::recursive() const
+{
+ return m_recursive;
+}
+
+void StructuredType::setRecursive(bool recursive)
+{
+ m_recursive = recursive;
+}
diff --git a/tools/datatypecodegenerator/structuredtype.h b/tools/datatypecodegenerator/structuredtype.h
new file mode 100644
index 0000000..13decd5
--- /dev/null
+++ b/tools/datatypecodegenerator/structuredtype.h
@@ -0,0 +1,56 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+#include <QtCore/qlist.h>
+
+class Field;
+
+class StructuredType : public XmlElement
+{
+public:
+ StructuredType(const QString &name, const QString &baseType);
+ ~StructuredType();
+
+ QString baseType() const;
+ void setBaseType(const QString &baseType);
+
+ void addField(Field *field);
+
+ void print() const override;
+ void accept(Visitor *visitor) override;
+
+ QList<Field *> fields() const;
+ void setFields(const QList<Field *> &fields);
+
+ bool containsBitMask() const;
+ void setContainsBitMask(bool containsBitMask);
+
+ bool isBuiltInType() const;
+ void setIsBuiltInType(bool isBuiltInType);
+
+ bool hasSwitchfield() const;
+ void setHasSwitchfield(bool hasSwitchfield);
+
+ bool hasUnion() const;
+ void setHasUnion(bool hasUnion);
+
+ QString targetNamespace() const;
+ void setTargetNamespace(const QString &targetNamespace);
+
+ bool recursive() const;
+ void setRecursive(bool recursive);
+
+private:
+ bool m_containsBitMask{false};
+ bool m_isBuiltInType{false};
+ bool m_hasSwitchfield{false};
+ bool m_hasUnion{false};
+ bool m_recursive{false};
+ QString m_baseType;
+ QString m_targetNamespace;
+ QList<Field *> m_fields;
+};
diff --git a/tools/datatypecodegenerator/typedictionary.cpp b/tools/datatypecodegenerator/typedictionary.cpp
new file mode 100644
index 0000000..7accd34
--- /dev/null
+++ b/tools/datatypecodegenerator/typedictionary.cpp
@@ -0,0 +1,116 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "typedictionary.h"
+
+#include "enumeratedtype.h"
+#include "import.h"
+#include "structuredtype.h"
+#include "visitor.h"
+
+#include <QtCore/qdebug.h>
+
+TypeDictionary::TypeDictionary(bool dependencyTypeDictionary,
+ const QString &defaultByOrder,
+ const QString &targetNamespace,
+ const QMap<QString, QString> &namespaces)
+ : m_dependencyTypeDictionary(dependencyTypeDictionary)
+ , m_defaultByOrder(defaultByOrder)
+ , m_targetNamespace(targetNamespace)
+ , m_namespaces(namespaces)
+{}
+
+TypeDictionary::~TypeDictionary()
+{
+ qDeleteAll(m_types);
+}
+
+QMap<QString, QString> TypeDictionary::namespaces() const
+{
+ return m_namespaces;
+}
+
+void TypeDictionary::setNamespaces(const QMap<QString, QString> &namespaces)
+{
+ m_namespaces = namespaces;
+}
+
+QString TypeDictionary::defaultByOrder() const
+{
+ return m_defaultByOrder;
+}
+
+void TypeDictionary::setDefaultByOrder(const QString &defaultByOrder)
+{
+ m_defaultByOrder = defaultByOrder;
+}
+
+QString TypeDictionary::targetNamespace() const
+{
+ return m_targetNamespace;
+}
+
+void TypeDictionary::setTargetNamespace(const QString &targetNamespace)
+{
+ m_targetNamespace = targetNamespace;
+}
+
+QList<const Import *> TypeDictionary::imports() const
+{
+ QList<const Import *> imports;
+ for (auto element : m_types) {
+ Import *import = dynamic_cast<Import *>(element);
+ if (import) {
+ imports.push_back(import);
+ }
+ }
+ return imports;
+}
+
+bool TypeDictionary::dependencyTypeDictionary() const
+{
+ return m_dependencyTypeDictionary;
+}
+
+QList<XmlElement *> TypeDictionary::types() const
+{
+ return m_types;
+}
+
+void TypeDictionary::addType(XmlElement *type)
+{
+ m_types.push_back(type);
+}
+
+void TypeDictionary::print() const
+{
+ for (auto it = m_namespaces.constBegin(); it != m_namespaces.constEnd(); ++it) {
+ qDebug() << it.key() << ": " << it.value();
+ }
+
+ qDebug() << "defaultByOrder:" << m_defaultByOrder << "\tTargetNamespace:" << m_targetNamespace;
+
+ for (auto element : m_types) {
+ const auto *enumeratedType = dynamic_cast<EnumeratedType *>(element);
+ if (enumeratedType) {
+ enumeratedType->print();
+ } else {
+ const auto *structuredType = dynamic_cast<StructuredType *>(element);
+ if (structuredType) {
+ structuredType->print();
+ } else {
+ const auto *import = dynamic_cast<Import *>(element);
+ if (import) {
+ import->print();
+ }
+ }
+ }
+ }
+}
+
+void TypeDictionary::accept(Visitor *visitor)
+{
+ visitor->visit(this);
+ for (const auto &element : m_types)
+ element->accept(visitor);
+}
diff --git a/tools/datatypecodegenerator/typedictionary.h b/tools/datatypecodegenerator/typedictionary.h
new file mode 100644
index 0000000..abab83e
--- /dev/null
+++ b/tools/datatypecodegenerator/typedictionary.h
@@ -0,0 +1,46 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qmap.h>
+
+class Import;
+class Visitor;
+class XmlElement;
+
+class TypeDictionary
+{
+public:
+ TypeDictionary(bool dependencyTypeDictionary,
+ const QString &defaultByOrder,
+ const QString &targetNamespace,
+ const QMap<QString, QString> &namespaces);
+ ~TypeDictionary();
+
+ void addType(XmlElement *type);
+ void print() const;
+ void accept(Visitor *visitor);
+
+ QMap<QString, QString> namespaces() const;
+ void setNamespaces(const QMap<QString, QString> &namespaces);
+
+ QString defaultByOrder() const;
+ void setDefaultByOrder(const QString &defaultByOrder);
+
+ QString targetNamespace() const;
+ void setTargetNamespace(const QString &targetNamespace);
+
+ QList<const Import *> imports() const;
+
+ bool dependencyTypeDictionary() const;
+
+ QList<XmlElement *> types() const;
+
+private:
+ bool m_dependencyTypeDictionary;
+ QString m_defaultByOrder;
+ QString m_targetNamespace;
+ QMap<QString, QString> m_namespaces;
+ QList<XmlElement *> m_types;
+};
diff --git a/tools/datatypecodegenerator/util.cpp b/tools/datatypecodegenerator/util.cpp
new file mode 100644
index 0000000..7a05e22
--- /dev/null
+++ b/tools/datatypecodegenerator/util.cpp
@@ -0,0 +1,31 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "util.h"
+
+#include <QtCore/qstringlist.h>
+
+QString Util::indent(int level)
+{
+ return QStringLiteral("%1").arg(" ", level * 4, ' ');
+}
+
+QString Util::lineBreak(int n)
+{
+ return QStringLiteral("%1").arg("\n", n, '\n');
+}
+
+QString Util::removeNamespace(const QString &typeName)
+{
+ return typeName.split(":").value(1, QString());
+}
+
+QString Util::lowerFirstLetter(const QString &temp)
+{
+ if (temp.isEmpty())
+ return temp;
+
+ auto result = temp;
+ result.front() = result.at(0).toLower();
+ return result;
+}
diff --git a/tools/datatypecodegenerator/util.h b/tools/datatypecodegenerator/util.h
new file mode 100644
index 0000000..5e877b1
--- /dev/null
+++ b/tools/datatypecodegenerator/util.h
@@ -0,0 +1,15 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qstring.h>
+
+namespace Util
+{
+ QString indent(int level);
+ QString lineBreak(int n = 1);
+
+ QString removeNamespace(const QString &typeName);
+ QString lowerFirstLetter(const QString &temp);
+}
diff --git a/tools/datatypecodegenerator/visitor.h b/tools/datatypecodegenerator/visitor.h
new file mode 100644
index 0000000..732ebda
--- /dev/null
+++ b/tools/datatypecodegenerator/visitor.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include "xmlelement.h"
+
+class EnumeratedType;
+class EnumeratedValue;
+class Import;
+class Field;
+class StructuredType;
+class TypeDictionary;
+
+class Visitor
+{
+public:
+ virtual ~Visitor() = default;
+ virtual void visit(XmlElement *xmlElement) = 0;
+ virtual void visit(EnumeratedType *enumteratedType) = 0;
+ virtual void visit(EnumeratedValue *enumeratedValue) = 0;
+ virtual void visit(Import *import) = 0;
+ virtual void visit(Field *field) = 0;
+ virtual void visit(StructuredType *structuredType) = 0;
+ virtual void visit(TypeDictionary *typeDictionary) = 0;
+};
diff --git a/tools/datatypecodegenerator/xmlelement.cpp b/tools/datatypecodegenerator/xmlelement.cpp
new file mode 100644
index 0000000..d28e036
--- /dev/null
+++ b/tools/datatypecodegenerator/xmlelement.cpp
@@ -0,0 +1,32 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#include "xmlelement.h"
+
+#include "util.h"
+
+#include <QtCore/qdebug.h>
+
+XmlElement::XmlElement(const QString &name)
+ : m_name(name)
+{}
+
+void XmlElement::setName(const QString &name)
+{
+ m_name = name;
+}
+
+QString XmlElement::name() const
+{
+ return m_name;
+}
+
+QString XmlElement::lowerFirstName() const
+{
+ return Util::lowerFirstLetter(m_name);
+}
+
+void XmlElement::print() const
+{
+ qDebug() << "name: " << m_name;
+}
diff --git a/tools/datatypecodegenerator/xmlelement.h b/tools/datatypecodegenerator/xmlelement.h
new file mode 100644
index 0000000..12809f2
--- /dev/null
+++ b/tools/datatypecodegenerator/xmlelement.h
@@ -0,0 +1,26 @@
+// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+
+#pragma once
+
+#include <QtCore/qstring.h>
+
+class Visitor;
+
+class XmlElement
+{
+public:
+ XmlElement() = default;
+ XmlElement(const QString &name);
+ virtual ~XmlElement() = default;
+
+ void setName(const QString &name);
+ QString name() const;
+ QString lowerFirstName() const;
+
+ virtual void print() const;
+ virtual void accept(Visitor *visitor) = 0;
+
+private:
+ QString m_name;
+};