1// Copyright (C) 2023 basysKom GmbH, opensource@basyskom.com
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtOpcUa/qopcuastructuredefinition.h>
5#include <QtOpcUa/qopcuagenericstructvalue.h>
6
7#include "private/qopcuagenericstructhandler_p.h"
8#include <QtOpcUa/qopcuabinarydataencoding.h>
9
10QT_BEGIN_NAMESPACE
11
12/*!
13 \class QOpcUaGenericStructHandler
14 \inmodule QtOpcUa
15 \since 6.7
16
17 \brief Reads a server's data types and decodes/encodes generic structs from/to extension objects.
18
19 The binary data encoding used in OPC UA was designed with a small message size in mind and doesn't
20 contain any information about the structure of the data.
21 This means that a decoder must known the structure of the encoded data in advance to be able to
22 decode a data buffer.
23
24 Since OPC UA 1.04, nodes of the DataType node class may have the DataTypeDefinition attribute
25 which contain information about the fields of structured types and the mapping of enum values
26 to names. Together with the knowledge about how to decode built-in types, this allows a client to
27 decode generic custom structured types without relying on outside knowledge.
28
29 QOpcUaGenericStructHandler traverses the type hierarchy of a server by following the HasSubtype
30 references starting from BaseDataType and reads the DataTypeDefinition attribute of the nodes.
31
32 For structured types where a \l QOpcUaStructureDefinition value is present in the DataTypeDefinition
33 attribute, automatic decoding of extension objects containing them is available. Fields with a built-in
34 type or a type where a C++ data class exists are deserialized to the corresponding Qt OPC UA type,
35 other generic structs are serialized to a nested \l QOpcUaGenericStructValue.
36 All nested generic struct values must have a \l QOpcUaStructureDefinition in the server or decoding fails.
37
38 The same conditions apply to encoding a custom struct.
39
40 Example for decoding a custom struct:
41
42 \code
43 QOpcUaGenericStructHandler handler(opcuaClient);
44 handler.initialize();
45
46 QObject::connect(&handler, &QOpcUaGenericStructHandler::initializedChanged, [opcuaClient, &handler](bool initialized) {
47 if (!initialized)
48 return;
49
50 auto node = opcuaClient->node("ns=4;i=3003"); // A custom struct test node in the open62541-testserver
51 node->readValueAttribute();
52
53 QObject::connect(node, &QOpcUaNode::attributeRead, [node, &handler](QOpcUa::NodeAttributes attr) {
54 if (!attr.testFlag(QOpcUa::NodeAttribute::Value) || node->valueAttributeError() != QOpcUa::UaStatusCode::Good)
55 return;
56
57 auto extObj = node->valueAttribute().value<QOpcUaExtensionObject>();
58 qDebug() << "Got object of type" << handler.typeNameForBinaryEncodingId(extObj.encodingTypeId());
59
60 const auto result = handler.decode(extObj);
61
62 if (!result)
63 return;
64
65 qDebug() << *result;
66 });
67 });
68 \endcode
69
70 Example for encoding a custom struct:
71
72 \code
73 QOpcUaGenericStructHandler handler(opcuaClient);
74 handler.initialize();
75
76 QObject::connect(&handler, &QOpcUaGenericStructHandler::initializedChanged, [opcuaClient, &handler](bool initialized) {
77 if (!initialized)
78 return;
79
80 QOpcUaGenericStructValue value = handler.createGenericStructValueForTypeId("ns=4;i=3006");
81 value.fieldsRef()["MandatoryMember"] = 23.0;
82 value.fieldsRef()["OptionalMember"] = 42.0;
83
84 const auto ext = handler.encode(value);
85
86 if (!ext)
87 return;
88
89 // Use the extension object to write a node's value attribute, in a method parameter, etc...
90 });
91 \endcode
92*/
93
94/*!
95 \enum QOpcUaGenericStructHandler::DataTypeKind
96
97 This enum type specifies data type kind of a data type node.
98
99 \value Unknown
100 The type node id is unknown.
101 \value Struct
102 The type node id belongs to a structured type.
103 \value Enum
104 The type node id belongs to an enum type.
105 \value Other
106 The type node id belongs to a type which is not a struct or enum (other built-in types or their subtypes)
107*/
108
109/*!
110 \fn QOpcUaGenericStructHandler::initializedChanged(bool initialized)
111
112 This signal is emitted when the initialization process has finished.
113 \a initialized indicates if the initialization was successful.
114*/
115
116/*!
117 Constructs a generic struct handler for \a client.
118*/
119QOpcUaGenericStructHandler::QOpcUaGenericStructHandler(QOpcUaClient *client, QObject *parent)
120 : QObject(*new QOpcUaGenericStructHandlerPrivate(client), parent)
121{
122
123}
124
125QOpcUaGenericStructHandler::~QOpcUaGenericStructHandler() = default;
126
127/*!
128 Starts the data type hierarchy traversal.
129 Success or failure is reported in the \l initializedChanged signal.
130
131 Returns \c false if the operation can't be started.
132*/
133bool QOpcUaGenericStructHandler::initialize()
134{
135 Q_D(QOpcUaGenericStructHandler);
136 return d->initialize();
137}
138
139/*!
140 Decodes \a extensionObject to a \l QOpcUaGenericStructValue. If the decoder fails, \c std::nullopt is returned.
141*/
142std::optional<QOpcUaGenericStructValue> QOpcUaGenericStructHandler::decode(const QOpcUaExtensionObject &extensionObject) const
143{
144 bool success = false;
145 auto result = d_func()->decode(extensionObject, success);
146 if (!success)
147 return std::nullopt;
148 return result;
149}
150
151/*!
152 Returns \a value encoded as a \l QOpcUaExtensionObject, or \c std::nullopt if the value could not be encoded.
153 */
154std::optional<QOpcUaExtensionObject> QOpcUaGenericStructHandler::encode(const QOpcUaGenericStructValue &value)
155{
156 QOpcUaExtensionObject output;
157 if (!d_func()->encode(value, output))
158 return std::nullopt;
159 return output;
160}
161
162/*!
163 Returns a generic struct value pre-filled with the struct definition, type id and type name
164 corresponding to \a typeId.
165 For all mandatory fields, an invalid placeholder \l QVariant will be inserted.
166 */
167QOpcUaGenericStructValue QOpcUaGenericStructHandler::createGenericStructValueForTypeId(const QString &typeId)
168{
169 return d_func()->createGenericStructValueForTypeId(typeId);
170}
171
172/*!
173 Returns the \l QOpcUaStructureDefinition for the binary encoding node id \a id.
174 If the node id is unknown or does not belong to a struct type, a default constructed value is returned.
175*/
176QOpcUaStructureDefinition QOpcUaGenericStructHandler::structureDefinitionForBinaryEncodingId(const QString &id) const
177{
178 return d_func()->structureDefinitionForBinaryEncodingId(id);
179}
180
181/*!
182 Returns the \l QOpcUaStructureDefinition for the type node id \a id.
183 If the node id is unknown or does not belong to a struct type, a default constructed value is returned.
184*/
185QOpcUaStructureDefinition QOpcUaGenericStructHandler::structureDefinitionForTypeId(const QString &id) const
186{
187 return d_func()->structureDefinitionForTypeId(id);
188}
189
190/*!
191 Returns the \ QOpcUaEnumDefinition for the type node id \a id.
192 If the node id is unknown or does not belong to an enum type, a default constructed value is returned.
193*/
194QOpcUaEnumDefinition QOpcUaGenericStructHandler::enumDefinitionForTypeId(const QString &id) const
195{
196 return d_func()->enumDefinitionForTypeId(id);
197}
198
199/*!
200 Returns the type name belonging to the binary encoding node id \a id.
201 If the node id is unknown or does not belong to a struct type, an empty string is returned.
202*/
203QString QOpcUaGenericStructHandler::typeNameForBinaryEncodingId(const QString &id) const
204{
205 return d_func()->typeNameForBinaryEncodingId(id);
206}
207
208/*!
209 Returns the type name belonging to a data type node identified by type node id \a id.
210 If the node id is unknown, an empty string is returned.
211*/
212QString QOpcUaGenericStructHandler::typeNameForTypeId(const QString &id) const
213{
214 return d_func()->typeNameForTypeId(id);
215}
216
217/*!
218 * Returns true if the data type described by \a id is abstract.
219 */
220bool QOpcUaGenericStructHandler::isAbstractTypeId(const QString &id) const
221{
222 return d_func()->isAbstractTypeId(id);
223}
224
225/*!
226 Adds the custom structure definition \a definition to the known types.
227 This can be used to support custom structures the server doesn't expose a StructureDefinition for.
228 The parameters \a definition, \a typeId and \a name are required for proper decoding
229 and encoding. If \a isAbstract is set, the type can't be encoded and decoded.
230
231 Returns \c true if the structure definition was successfully added.
232 */
233bool QOpcUaGenericStructHandler::addCustomStructureDefinition(const QOpcUaStructureDefinition &definition, const QString &typeId,
234 const QString &name, QOpcUa::IsAbstract isAbstract)
235{
236 return d_func()->addCustomStructureDefinition(definition, typeId, name, isAbstract);
237}
238
239/*!
240 Adds the custom enum definition \a definition to the known types.
241 This can be used to support custom structures the server doesn't expose a StructureDefinition for.
242 The parameters \a definition, \a typeId and \a name are required for proper decoding
243 and encoding. If \a isAbstract is set, the type can't be encoded and decoded.
244
245 Returns \c true if the enum definition was successfully added.
246 */
247bool QOpcUaGenericStructHandler::addCustomEnumDefinition(const QOpcUaEnumDefinition &definition, const QString &typeId,
248 const QString &name, QOpcUa::IsAbstract isAbstract)
249{
250 return d_func()->addCustomEnumDefinition(definition, typeId, name, isAbstract);
251}
252
253/*!
254 \since 6.7
255
256 Returns \c true if the generic struct handler is initialized.
257*/
258bool QOpcUaGenericStructHandler::initialized() const
259{
260 return d_func()->initialized();
261}
262
263/*!
264 Returns the data type kind for the type node id \a id.
265*/
266QOpcUaGenericStructHandler::DataTypeKind QOpcUaGenericStructHandler::dataTypeKindForTypeId(const QString &id) const
267{
268 return d_func()->dataTypeKindForTypeId(id);
269}
270
271/*!
272 Returns the type node id associated with the binary encoding id \a id.
273*/
274QString QOpcUaGenericStructHandler::typeIdForBinaryEncodingId(const QString &id) const
275{
276 return d_func()->typeIdForBinaryEncodingId(id);
277}
278
279QT_END_NAMESPACE
280

Provided by KDAB

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

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