1// Copyright (C) 2017 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#ifdef USE_SYSTEM_OPEN62541
5#include <open62541/types.h>
6#else
7#include "qopen62541.h"
8#endif
9
10#include "qopen62541utils.h"
11#include "qopen62541valueconverter.h"
12
13#include "qopcuadatavalue.h"
14#include "qopcuamultidimensionalarray.h"
15#include "qopcuastructuredefinition.h"
16#include "qopcuaenumdefinition.h"
17
18#include <QtOpcUa/qopcuaargument.h>
19#include <QtOpcUa/qopcuaattributeoperand.h>
20#include <QtOpcUa/qopcuaaxisinformation.h>
21#include <QtOpcUa/qopcuacomplexnumber.h>
22#include <QtOpcUa/qopcuadiagnosticinfo.h>
23#include <QtOpcUa/qopcuadoublecomplexnumber.h>
24#include <QtOpcUa/qopcuaelementoperand.h>
25#include <QtOpcUa/qopcuaenumfield.h>
26#include <QtOpcUa/qopcuaeuinformation.h>
27#include <QtOpcUa/qopcualiteraloperand.h>
28#include <QtOpcUa/qopcualocalizedtext.h>
29#include <QtOpcUa/qopcuaqualifiedname.h>
30#include <QtOpcUa/qopcuarange.h>
31#include <QtOpcUa/qopcuastructurefield.h>
32#include <QtOpcUa/qopcuaxvalue.h>
33
34#include <QtCore/qdatetime.h>
35#include <QtCore/qloggingcategory.h>
36#include <QtCore/qtimezone.h>
37#include <QtCore/quuid.h>
38
39#include <cstring>
40
41QT_BEGIN_NAMESPACE
42
43Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541)
44
45using namespace QOpcUa::NodeIds;
46
47namespace QOpen62541ValueConverter {
48
49UA_Variant toOpen62541Variant(const QVariant &value, QOpcUa::Types type)
50{
51 UA_Variant open62541value;
52 UA_Variant_init(p: &open62541value);
53
54 if (value.canConvert<QOpcUaMultiDimensionalArray>()) {
55 QOpcUaMultiDimensionalArray data = value.value<QOpcUaMultiDimensionalArray>();
56 UA_Variant result = toOpen62541Variant(value: data.valueArray(), type);
57
58 const auto &arrayDimensions = data.arrayDimensions();
59
60 if (!arrayDimensions.isEmpty()) {
61 // Ensure that the array dimensions size is < UINT32_MAX
62 if (static_cast<quint64>(arrayDimensions.size()) > (std::numeric_limits<quint32>::max)())
63 return open62541value;
64 result.arrayDimensionsSize = arrayDimensions.size();
65 result.arrayDimensions = static_cast<UA_UInt32 *>(UA_Array_new(size: result.arrayDimensionsSize, type: &UA_TYPES[UA_TYPES_UINT32]));
66 std::copy(first: arrayDimensions.constBegin(), last: arrayDimensions.constEnd(), result: result.arrayDimensions);
67 }
68 return result;
69 }
70
71 if (value.metaType().id() == QMetaType::QVariantList && value.toList().size() == 0)
72 return open62541value;
73
74 QVariant temp = (value.metaType().id() == QMetaType::QVariantList) ? value.toList().at(i: 0) : value;
75 QOpcUa::Types valueType = type == QOpcUa::Undefined ?
76 QOpcUa::metaTypeToQOpcUaType(type: static_cast<QMetaType::Type>(temp.metaType().id())) : type;
77
78 const UA_DataType *dt = toDataType(valueType);
79
80 switch (valueType) {
81 case QOpcUa::Boolean:
82 return arrayFromQVariant<UA_Boolean, bool>(var: value, type: dt);
83 case QOpcUa::SByte:
84 return arrayFromQVariant<UA_SByte, char>(var: value, type: dt);
85 case QOpcUa::Byte:
86 return arrayFromQVariant<UA_Byte, uchar>(var: value, type: dt);
87 case QOpcUa::Int16:
88 return arrayFromQVariant<UA_Int16, qint16>(var: value, type: dt);
89 case QOpcUa::UInt16:
90 return arrayFromQVariant<UA_UInt16, quint16>(var: value, type: dt);
91 case QOpcUa::Int32:
92 return arrayFromQVariant<UA_Int32, qint32>(var: value, type: dt);
93 case QOpcUa::UInt32:
94 return arrayFromQVariant<UA_UInt32, quint32>(var: value, type: dt);
95 case QOpcUa::Int64:
96 return arrayFromQVariant<UA_Int64, int64_t>(var: value, type: dt);
97 case QOpcUa::UInt64:
98 return arrayFromQVariant<UA_UInt64, uint64_t>(var: value, type: dt);
99 case QOpcUa::Float:
100 return arrayFromQVariant<UA_Float, float>(var: value, type: dt);
101 case QOpcUa::Double:
102 return arrayFromQVariant<UA_Double, double>(var: value, type: dt);
103 case QOpcUa::DateTime:
104 return arrayFromQVariant<UA_DateTime, QDateTime>(var: value, type: dt);
105 case QOpcUa::String:
106 return arrayFromQVariant<UA_String, QString>(var: value, type: dt);
107 case QOpcUa::LocalizedText:
108 return arrayFromQVariant<UA_LocalizedText, QOpcUaLocalizedText>(var: value, type: dt);
109 case QOpcUa::ByteString:
110 return arrayFromQVariant<UA_ByteString, QByteArray>(var: value, type: dt);
111 case QOpcUa::NodeId:
112 return arrayFromQVariant<UA_NodeId, QString>(var: value, type: dt);
113 case QOpcUa::Guid:
114 return arrayFromQVariant<UA_Guid, QUuid>(var: value, type: dt);
115 case QOpcUa::XmlElement:
116 return arrayFromQVariant<UA_XmlElement, QString>(var: value, type: dt);
117 case QOpcUa::QualifiedName:
118 return arrayFromQVariant<UA_QualifiedName, QOpcUaQualifiedName>(var: value, type: dt);
119 case QOpcUa::StatusCode:
120 return arrayFromQVariant<UA_StatusCode, QOpcUa::UaStatusCode>(var: value, type: dt);
121 case QOpcUa::Range:
122 return arrayFromQVariant<UA_Range, QOpcUaRange>(var: value, type: dt);
123 case QOpcUa::EUInformation:
124 return arrayFromQVariant<UA_EUInformation, QOpcUaEUInformation>(var: value, type: dt);
125 case QOpcUa::ComplexNumber:
126 return arrayFromQVariant<UA_ComplexNumberType, QOpcUaComplexNumber>(var: value, type: dt);
127 case QOpcUa::DoubleComplexNumber:
128 return arrayFromQVariant<UA_DoubleComplexNumberType, QOpcUaDoubleComplexNumber>(var: value, type: dt);
129 case QOpcUa::AxisInformation:
130 return arrayFromQVariant<UA_AxisInformation, QOpcUaAxisInformation>(var: value, type: dt);
131 case QOpcUa::XV:
132 return arrayFromQVariant<UA_XVType, QOpcUaXValue>(var: value, type: dt);
133 case QOpcUa::ExpandedNodeId:
134 return arrayFromQVariant<UA_ExpandedNodeId, QOpcUaExpandedNodeId>(var: value, type: dt);
135 case QOpcUa::Argument:
136 return arrayFromQVariant<UA_Argument, QOpcUaArgument>(var: value, type: dt);
137 case QOpcUa::SimpleAttributeOperand:
138 return arrayFromQVariant<UA_SimpleAttributeOperand, QOpcUaSimpleAttributeOperand>(var: value, type: dt);
139 case QOpcUa::AttributeOperand:
140 return arrayFromQVariant<UA_AttributeOperand, QOpcUaAttributeOperand>(var: value, type: dt);
141 case QOpcUa::LiteralOperand:
142 return arrayFromQVariant<UA_LiteralOperand, QOpcUaLiteralOperand>(var: value, type: dt);
143 case QOpcUa::ElementOperand:
144 return arrayFromQVariant<UA_SimpleAttributeOperand, QOpcUaSimpleAttributeOperand>(var: value, type: dt);
145 case QOpcUa::RelativePathElement:
146 return arrayFromQVariant<UA_RelativePathElement, QOpcUaRelativePathElement>(var: value, type: dt);
147 case QOpcUa::ContentFilterElement:
148 return arrayFromQVariant<UA_ContentFilterElement, QOpcUaContentFilterElement>(var: value, type: dt);
149 case QOpcUa::EventFilter:
150 return arrayFromQVariant<UA_EventFilter, QOpcUaMonitoringParameters::EventFilter>(var: value, type: dt);
151 case QOpcUa::ExtensionObject:
152 return arrayFromQVariant<UA_ExtensionObject, QOpcUaExtensionObject>(var: value, type: dt);
153 case QOpcUa::StructureDefinition:
154 return arrayFromQVariant<UA_StructureDefinition, QOpcUaStructureDefinition>(var: value, type: dt);
155 case QOpcUa::StructureField:
156 return arrayFromQVariant<UA_StructureField, QOpcUaStructureField>(var: value, type: dt);
157 case QOpcUa::EnumDefinition:
158 return arrayFromQVariant<UA_EnumDefinition, QOpcUaEnumDefinition>(var: value, type: dt);
159 case QOpcUa::EnumField:
160 return arrayFromQVariant<UA_EnumField, QOpcUaEnumField>(var: value, type: dt);
161 case QOpcUa::DiagnosticInfo:
162 return arrayFromQVariant<UA_DiagnosticInfo, QOpcUaDiagnosticInfo>(var: value, type: dt);
163 default:
164 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Variant conversion to Open62541 for typeIndex" << type << " not implemented";
165 }
166
167 return open62541value;
168}
169
170QVariant toQVariant(const UA_Variant &value)
171{
172 if (value.type == nullptr) {
173 return QVariant();
174 }
175
176 if (value.type == &UA_TYPES[UA_TYPES_BOOLEAN])
177 return arrayToQVariant<bool, UA_Boolean>(var: value, type: QMetaType::Bool);
178 else if (value.type == &UA_TYPES[UA_TYPES_SBYTE])
179 return arrayToQVariant<signed char, UA_SByte>(var: value, type: QMetaType::SChar);
180 else if (value.type == &UA_TYPES[UA_TYPES_BYTE])
181 return arrayToQVariant<uchar, UA_Byte>(var: value, type: QMetaType::UChar);
182 else if (value.type == &UA_TYPES[UA_TYPES_INT16])
183 return arrayToQVariant<qint16, UA_Int16>(var: value, type: QMetaType::Short);
184 else if (value.type == &UA_TYPES[UA_TYPES_UINT16])
185 return arrayToQVariant<quint16, UA_UInt16>(var: value, type: QMetaType::UShort);
186 else if (value.type == &UA_TYPES[UA_TYPES_INT32])
187 return arrayToQVariant<qint32, UA_Int32>(var: value, type: QMetaType::Int);
188 else if (value.type == &UA_TYPES[UA_TYPES_UINT32])
189 return arrayToQVariant<quint32, UA_UInt32>(var: value, type: QMetaType::UInt);
190 else if (value.type == &UA_TYPES[UA_TYPES_INT64])
191 return arrayToQVariant<int64_t, UA_Int64>(var: value, type: QMetaType::LongLong);
192 else if (value.type == &UA_TYPES[UA_TYPES_UINT64])
193 return arrayToQVariant<uint64_t, UA_UInt64>(var: value, type: QMetaType::ULongLong);
194 else if (value.type == &UA_TYPES[UA_TYPES_FLOAT])
195 return arrayToQVariant<float, UA_Float>(var: value, type: QMetaType::Float);
196 else if (value.type == &UA_TYPES[UA_TYPES_DOUBLE])
197 return arrayToQVariant<double, UA_Double>(var: value, type: QMetaType::Double);
198 else if (value.type == &UA_TYPES[UA_TYPES_STRING])
199 return arrayToQVariant<QString, UA_String>(var: value, type: QMetaType::QString);
200 else if (value.type == &UA_TYPES[UA_TYPES_BYTESTRING])
201 return arrayToQVariant<QByteArray, UA_ByteString>(var: value, type: QMetaType::QByteArray);
202 else if (value.type == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])
203 return arrayToQVariant<QOpcUaLocalizedText, UA_LocalizedText>(var: value);
204 else if (value.type == &UA_TYPES[UA_TYPES_NODEID])
205 return arrayToQVariant<QString, UA_NodeId>(var: value, type: QMetaType::QString);
206 else if (value.type == &UA_TYPES[UA_TYPES_DATETIME])
207 return arrayToQVariant<QDateTime, UA_DateTime>(var: value, type: QMetaType::QDateTime);
208 else if (value.type == &UA_TYPES[UA_TYPES_GUID])
209 return arrayToQVariant<QUuid, UA_Guid>(var: value, type: QMetaType::QUuid);
210 else if (value.type == &UA_TYPES[UA_TYPES_XMLELEMENT])
211 return arrayToQVariant<QString, UA_XmlElement>(var: value, type: QMetaType::QString);
212 else if (value.type == &UA_TYPES[UA_TYPES_QUALIFIEDNAME])
213 return arrayToQVariant<QOpcUaQualifiedName, UA_QualifiedName>(var: value);
214 else if (value.type == &UA_TYPES[UA_TYPES_STATUSCODE])
215 return arrayToQVariant<QOpcUa::UaStatusCode, UA_StatusCode>(var: value, type: QMetaType::UInt);
216 else if (value.type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
217 return arrayToQVariant<QVariant, UA_ExtensionObject>(var: value);
218 else if (value.type == &UA_TYPES[UA_TYPES_EXPANDEDNODEID])
219 return arrayToQVariant<QOpcUaExpandedNodeId, UA_ExpandedNodeId>(var: value);
220 else if (value.type == &UA_TYPES[UA_TYPES_ARGUMENT])
221 return arrayToQVariant<QOpcUaArgument, UA_Argument>(var: value);
222 else if (value.type == &UA_TYPES[UA_TYPES_RANGE])
223 return arrayToQVariant<QOpcUaRange, UA_Range>(var: value);
224 else if (value.type == &UA_TYPES[UA_TYPES_EUINFORMATION])
225 return arrayToQVariant<QOpcUaEUInformation, UA_EUInformation>(var: value);
226 else if (value.type == &UA_TYPES[UA_TYPES_AXISINFORMATION])
227 return arrayToQVariant<QOpcUaAxisInformation, UA_AxisInformation>(var: value);
228 else if (value.type == &UA_TYPES[UA_TYPES_COMPLEXNUMBERTYPE])
229 return arrayToQVariant<QOpcUaComplexNumber, UA_ComplexNumberType>(var: value);
230 else if (value.type == &UA_TYPES[UA_TYPES_DOUBLECOMPLEXNUMBERTYPE])
231 return arrayToQVariant<QOpcUaDoubleComplexNumber, UA_DoubleComplexNumberType>(var: value);
232 else if (value.type == &UA_TYPES[UA_TYPES_XVTYPE])
233 return arrayToQVariant<QOpcUaXValue, UA_XVType>(var: value);
234 else if (value.type == &UA_TYPES[UA_TYPES_STRUCTUREDEFINITION])
235 return arrayToQVariant<QOpcUaStructureDefinition, UA_StructureDefinition>(var: value);
236 else if (value.type == &UA_TYPES[UA_TYPES_STRUCTUREFIELD])
237 return arrayToQVariant<QOpcUaStructureField, UA_StructureField>(var: value);
238 else if (value.type == &UA_TYPES[UA_TYPES_ENUMDEFINITION])
239 return arrayToQVariant<QOpcUaEnumDefinition, UA_EnumDefinition>(var: value);
240 else if (value.type == &UA_TYPES[UA_TYPES_ENUMFIELD])
241 return arrayToQVariant<QOpcUaEnumField, UA_EnumField>(var: value);
242 else if (value.type == &UA_TYPES[UA_TYPES_DIAGNOSTICINFO])
243 return arrayToQVariant<QOpcUaDiagnosticInfo, UA_DiagnosticInfo>(var: value);
244 else if (value.type == &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND])
245 return arrayToQVariant<QOpcUaSimpleAttributeOperand, UA_SimpleAttributeOperand>(var: value);
246 else if (value.type == &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND])
247 return arrayToQVariant<QOpcUaSimpleAttributeOperand, UA_SimpleAttributeOperand>(var: value);
248 else if (value.type == &UA_TYPES[UA_TYPES_LITERALOPERAND])
249 return arrayToQVariant<QOpcUaLiteralOperand, UA_LiteralOperand>(var: value);
250 else if (value.type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND])
251 return arrayToQVariant<QOpcUaElementOperand, UA_ElementOperand>(var: value);
252 else if (value.type == &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT])
253 return arrayToQVariant<QOpcUaRelativePathElement, UA_RelativePathElement>(var: value);
254 else if (value.type == &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT])
255 return arrayToQVariant<QOpcUaContentFilterElement, UA_ContentFilterElement>(var: value);
256 else if (value.type == &UA_TYPES[UA_TYPES_EVENTFILTER])
257 return arrayToQVariant<QOpcUaMonitoringParameters::EventFilter, UA_EventFilter>(var: value);
258
259 return uaVariantToQtExtensionObject(var: value);
260}
261
262const UA_DataType *toDataType(QOpcUa::Types valueType)
263{
264 switch (valueType) {
265 case QOpcUa::Boolean:
266 return &UA_TYPES[UA_TYPES_BOOLEAN];
267 case QOpcUa::Int32:
268 return &UA_TYPES[UA_TYPES_INT32];
269 case QOpcUa::UInt32:
270 return &UA_TYPES[UA_TYPES_UINT32];
271 case QOpcUa::Double:
272 return &UA_TYPES[UA_TYPES_DOUBLE];
273 case QOpcUa::Float:
274 return &UA_TYPES[UA_TYPES_FLOAT];
275 case QOpcUa::String:
276 return &UA_TYPES[UA_TYPES_STRING];
277 case QOpcUa::LocalizedText:
278 return &UA_TYPES[UA_TYPES_LOCALIZEDTEXT];
279 case QOpcUa::DateTime:
280 return &UA_TYPES[UA_TYPES_DATETIME];
281 case QOpcUa::UInt16:
282 return &UA_TYPES[UA_TYPES_UINT16];
283 case QOpcUa::Int16:
284 return &UA_TYPES[UA_TYPES_INT16];
285 case QOpcUa::UInt64:
286 return &UA_TYPES[UA_TYPES_UINT64];
287 case QOpcUa::Int64:
288 return &UA_TYPES[UA_TYPES_INT64];
289 case QOpcUa::Byte:
290 return &UA_TYPES[UA_TYPES_BYTE];
291 case QOpcUa::SByte:
292 return &UA_TYPES[UA_TYPES_SBYTE];
293 case QOpcUa::ByteString:
294 return &UA_TYPES[UA_TYPES_BYTESTRING];
295 case QOpcUa::XmlElement:
296 return &UA_TYPES[UA_TYPES_XMLELEMENT];
297 case QOpcUa::NodeId:
298 return &UA_TYPES[UA_TYPES_NODEID];
299 case QOpcUa::Guid:
300 return &UA_TYPES[UA_TYPES_GUID];
301 case QOpcUa::QualifiedName:
302 return &UA_TYPES[UA_TYPES_QUALIFIEDNAME];
303 case QOpcUa::StatusCode:
304 return &UA_TYPES[UA_TYPES_STATUSCODE];
305 case QOpcUa::Range:
306 return &UA_TYPES[UA_TYPES_RANGE];
307 case QOpcUa::EUInformation:
308 return &UA_TYPES[UA_TYPES_EUINFORMATION];
309 case QOpcUa::ComplexNumber:
310 return &UA_TYPES[UA_TYPES_COMPLEXNUMBERTYPE];
311 case QOpcUa::DoubleComplexNumber:
312 return &UA_TYPES[UA_TYPES_DOUBLECOMPLEXNUMBERTYPE];
313 case QOpcUa::AxisInformation:
314 return &UA_TYPES[UA_TYPES_AXISINFORMATION];
315 case QOpcUa::XV:
316 return &UA_TYPES[UA_TYPES_XVTYPE];
317 case QOpcUa::ExtensionObject:
318 return &UA_TYPES[UA_TYPES_EXTENSIONOBJECT];
319 case QOpcUa::ExpandedNodeId:
320 return &UA_TYPES[UA_TYPES_EXPANDEDNODEID];
321 case QOpcUa::Argument:
322 return &UA_TYPES[UA_TYPES_ARGUMENT];
323 case QOpcUa::StructureDefinition:
324 return &UA_TYPES[UA_TYPES_STRUCTUREDEFINITION];
325 case QOpcUa::StructureField:
326 return &UA_TYPES[UA_TYPES_STRUCTUREFIELD];
327 case QOpcUa::EnumDefinition:
328 return &UA_TYPES[UA_TYPES_ENUMDEFINITION];
329 case QOpcUa::EnumField:
330 return &UA_TYPES[UA_TYPES_ENUMFIELD];
331 case QOpcUa::DiagnosticInfo:
332 return &UA_TYPES[UA_TYPES_DIAGNOSTICINFO];
333 case QOpcUa::SimpleAttributeOperand:
334 return &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND];
335 case QOpcUa::AttributeOperand:
336 return &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND];
337 case QOpcUa::LiteralOperand:
338 return &UA_TYPES[UA_TYPES_LITERALOPERAND];
339 case QOpcUa::ElementOperand:
340 return &UA_TYPES[UA_TYPES_ELEMENTOPERAND];
341 case QOpcUa::RelativePathElement:
342 return &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT];
343 case QOpcUa::ContentFilterElement:
344 return &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT];
345 case QOpcUa::EventFilter:
346 return &UA_TYPES[UA_TYPES_EVENTFILTER];
347 default:
348 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Trying to convert undefined type:" << valueType;
349 return nullptr;
350 }
351}
352
353template<typename TARGETTYPE, typename UATYPE>
354TARGETTYPE scalarToQt(const UATYPE *data)
355{
356 return *reinterpret_cast<const TARGETTYPE *>(data);
357}
358
359template<>
360QString scalarToQt<QString, UA_String>(const UA_String *data)
361{
362 return QString::fromUtf8(utf8: reinterpret_cast<const char *>(data->data), size: data->length);
363}
364
365template<>
366QByteArray scalarToQt<QByteArray, UA_ByteString>(const UA_ByteString *data)
367{
368 return QByteArray(reinterpret_cast<const char *>(data->data), data->length);
369}
370
371template<>
372QOpcUaLocalizedText scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(const UA_LocalizedText *data)
373{
374 QOpcUaLocalizedText lt;
375 lt.setLocale(scalarToQt<QString, UA_String>(data: &(data->locale)));
376 lt.setText(scalarToQt<QString, UA_String>(data: &(data->text)));
377 return lt;
378}
379
380template<>
381QString scalarToQt<QString, UA_NodeId>(const UA_NodeId *data)
382{
383 return Open62541Utils::nodeIdToQString(id: *data);
384}
385
386template<>
387QDateTime scalarToQt<QDateTime, UA_DateTime>(const UA_DateTime *data)
388{
389 // OPC UA 1.05 part 6, 5.1.4
390 if (*data == (std::numeric_limits<qint64>::min)() || *data == (std::numeric_limits<qint64>::max)())
391 return QDateTime();
392
393 const QDateTime epochStart(QDate(1601, 1, 1), QTime(0, 0), QTimeZone::UTC);
394 return epochStart.addMSecs(msecs: *data / UA_DATETIME_MSEC).toLocalTime();
395}
396
397template<>
398QOpcUaDataValue scalarToQt<QOpcUaDataValue, UA_DataValue>(const UA_DataValue *data)
399{
400 QOpcUaDataValue result;
401 if (data->hasSourceTimestamp)
402 result.setSourceTimestamp(QOpen62541ValueConverter::scalarToQt<QDateTime, UA_DateTime>(data: &data->sourceTimestamp));
403 if (data->hasServerTimestamp)
404 result.setServerTimestamp(QOpen62541ValueConverter::scalarToQt<QDateTime, UA_DateTime>(data: &data->serverTimestamp));
405 if (data->hasValue)
406 result.setValue(QOpen62541ValueConverter::toQVariant(value: data->value));
407 if (data->hasStatus) {
408 result.setStatusCode(QOpen62541ValueConverter::scalarToQt<QOpcUa::UaStatusCode, UA_StatusCode>(data: &data->status));
409 } else {
410 result.setStatusCode(QOpcUa::UaStatusCode::Good);
411 }
412 if (data->hasServerPicoseconds)
413 result.setServerPicoseconds(data->serverPicoseconds);
414 if (data->hasSourcePicoseconds)
415 result.setSourcePicoseconds(data->sourcePicoseconds);
416
417 return result;
418}
419
420template<>
421QUuid scalarToQt<QUuid, UA_Guid>(const UA_Guid *data)
422{
423 return QUuid(data->data1, data->data2, data->data3, data->data4[0], data->data4[1], data->data4[2],
424 data->data4[3], data->data4[4], data->data4[5], data->data4[6], data->data4[7]);
425}
426
427template<>
428QOpcUaQualifiedName scalarToQt<QOpcUaQualifiedName, UA_QualifiedName>(const UA_QualifiedName *data)
429{
430 QOpcUaQualifiedName temp;
431 temp.setNamespaceIndex(data->namespaceIndex);
432 temp.setName(scalarToQt<QString, UA_String>(data: &(data->name)));
433 return temp;
434}
435
436template<>
437QOpcUaArgument scalarToQt<QOpcUaArgument, UA_Argument>(const UA_Argument *data)
438{
439 QOpcUaArgument temp;
440 temp.setValueRank(data->valueRank);
441 temp.setDataTypeId(Open62541Utils::nodeIdToQString(id: data->dataType));
442 temp.setName(scalarToQt<QString, UA_String>(data: &data->name));
443 temp.setDescription(scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->description));
444 for (size_t i = 0; i < data->arrayDimensionsSize; ++i)
445 temp.arrayDimensionsRef().append(t: data->arrayDimensions[i]);
446 return temp;
447}
448
449template<>
450QOpcUaRange scalarToQt<QOpcUaRange, UA_Range>(const UA_Range *data)
451{
452 return QOpcUaRange(data->low, data->high);
453}
454
455template<>
456QOpcUaEUInformation scalarToQt<QOpcUaEUInformation, UA_EUInformation>(const UA_EUInformation *data)
457{
458 return QOpcUaEUInformation(scalarToQt<QString, UA_String>(data: &data->namespaceUri),
459 data->unitId,
460 scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->displayName),
461 scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->description));
462}
463
464template<>
465QOpcUaComplexNumber scalarToQt<QOpcUaComplexNumber, UA_ComplexNumberType>(const UA_ComplexNumberType *data)
466{
467 return QOpcUaComplexNumber(data->real, data->imaginary);
468}
469
470template<>
471QOpcUaDoubleComplexNumber scalarToQt<QOpcUaDoubleComplexNumber, UA_DoubleComplexNumberType>(
472 const UA_DoubleComplexNumberType *data)
473{
474 return QOpcUaDoubleComplexNumber(data->real, data->imaginary);
475}
476
477template<>
478QOpcUaAxisInformation scalarToQt<QOpcUaAxisInformation, UA_AxisInformation>(const UA_AxisInformation *data)
479{
480 QList<double> axisSteps;
481
482 if (data->axisStepsSize) {
483 axisSteps.reserve(asize: data->axisStepsSize);
484 std::copy(first: data->axisSteps, last: data->axisSteps + data->axisStepsSize, result: std::back_inserter(x&: axisSteps));
485 }
486
487 return QOpcUaAxisInformation(scalarToQt<QOpcUaEUInformation, UA_EUInformation>(data: &data->engineeringUnits),
488 scalarToQt<QOpcUaRange, UA_Range>(data: &data->eURange),
489 scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->title),
490 static_cast<QOpcUa::AxisScale>(data->axisScaleType),
491 axisSteps);
492}
493
494template<>
495QOpcUaXValue scalarToQt<QOpcUaXValue, UA_XVType>(const UA_XVType *data)
496{
497 return QOpcUaXValue(data->x, data->value);
498}
499
500template<>
501QOpcUaStructureField scalarToQt<QOpcUaStructureField, UA_StructureField>(const UA_StructureField *data)
502{
503 QOpcUaStructureField temp;
504 temp.setName(scalarToQt<QString, UA_String>(data: &data->name));
505 temp.setDescription(scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->description));
506 temp.setDataType(scalarToQt<QString, UA_NodeId>(data: &data->dataType));
507 temp.setIsOptional(data->isOptional);
508 temp.setMaxStringLength(scalarToQt<quint32, UA_UInt32>(data: &data->maxStringLength));
509 temp.setValueRank(scalarToQt<qint32, UA_Int32>(data: &data->valueRank));
510
511 QList<quint32> dimensions;
512 for (size_t i = 0; i < data->arrayDimensionsSize; ++i)
513 dimensions.append(t: data->arrayDimensions[i]);
514 temp.setArrayDimensions(dimensions);
515 return temp;
516}
517
518template<>
519QOpcUaStructureDefinition scalarToQt<QOpcUaStructureDefinition, UA_StructureDefinition>(const UA_StructureDefinition *data)
520{
521 QOpcUaStructureDefinition temp;
522 temp.setBaseDataType(scalarToQt<QString, UA_NodeId>(data: &data->baseDataType));
523 temp.setDefaultEncodingId(scalarToQt<QString, UA_NodeId>(data: &data->defaultEncodingId));
524 temp.setStructureType(static_cast<QOpcUaStructureDefinition::StructureType>(data->structureType));
525
526 QList<QOpcUaStructureField> fields;
527 for (size_t i = 0; i < data->fieldsSize; ++i)
528 fields.append(t: scalarToQt<QOpcUaStructureField, UA_StructureField>(data: &data->fields[i]));
529 temp.setFields(fields);
530 return temp;
531}
532
533template<>
534QOpcUaEnumField scalarToQt<QOpcUaEnumField, UA_EnumField>(const UA_EnumField *data)
535{
536 QOpcUaEnumField temp;
537 temp.setName(scalarToQt<QString, UA_String>(data: &data->name));
538 temp.setDisplayName(scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->displayName));
539 temp.setValue(data->value);
540 temp.setDescription(scalarToQt<QOpcUaLocalizedText, UA_LocalizedText>(data: &data->description));
541
542 return temp;
543}
544
545template<>
546QOpcUaEnumDefinition scalarToQt<QOpcUaEnumDefinition, UA_EnumDefinition>(const UA_EnumDefinition *data)
547{
548 QOpcUaEnumDefinition temp;
549
550 QList<QOpcUaEnumField> fields;
551 for (size_t i = 0; i < data->fieldsSize; ++i)
552 fields.push_back(t: scalarToQt<QOpcUaEnumField, UA_EnumField>(data: &data->fields[i]));
553 temp.setFields(fields);
554
555 return temp;
556}
557
558template<>
559QOpcUaDiagnosticInfo scalarToQt<QOpcUaDiagnosticInfo, UA_DiagnosticInfo>(const UA_DiagnosticInfo *data)
560{
561 QOpcUaDiagnosticInfo temp;
562
563 if (data->hasSymbolicId) {
564 temp.setHasSymbolicId(true);
565 temp.setSymbolicId(data->symbolicId);
566 }
567
568 if (data->hasNamespaceUri) {
569 temp.setHasNamespaceUri(true);
570 temp.setNamespaceUri(data->namespaceUri);
571 }
572
573 if (data->hasLocale) {
574 temp.setHasLocale(true);
575 temp.setLocale(data->locale);
576 }
577
578 if (data->hasLocalizedText) {
579 temp.setHasLocalizedText(true);
580 temp.setLocalizedText(data->localizedText);
581 }
582
583 if (data->hasAdditionalInfo) {
584 temp.setHasAdditionalInfo(true);
585 temp.setAdditionalInfo(scalarToQt<QString, UA_String>(data: &data->additionalInfo));
586 }
587
588 if (data->hasInnerStatusCode) {
589 temp.setHasInnerStatusCode(true);
590 temp.setInnerStatusCode(scalarToQt<QOpcUa::UaStatusCode, UA_StatusCode>(data: &data->innerStatusCode));
591 }
592
593 if (data->hasInnerDiagnosticInfo) {
594 temp.setHasInnerDiagnosticInfo(true);
595 if (data->innerDiagnosticInfo)
596 temp.setInnerDiagnosticInfo(scalarToQt<QOpcUaDiagnosticInfo, UA_DiagnosticInfo>(data: data->innerDiagnosticInfo));
597 }
598
599 return temp;
600}
601
602template<>
603QOpcUaSimpleAttributeOperand scalarToQt<QOpcUaSimpleAttributeOperand, UA_SimpleAttributeOperand>(const UA_SimpleAttributeOperand *data)
604{
605 QOpcUaSimpleAttributeOperand result;
606
607 result.setAttributeId(toQtAttributeId(attr: static_cast<UA_AttributeId>(data->attributeId)));
608 result.setIndexRange(scalarToQt<QString, UA_String>(data: &data->indexRange));
609 result.setTypeId(scalarToQt<QString, UA_NodeId>(data: &data->typeDefinitionId));
610
611 QList<QOpcUaQualifiedName> browsePath;
612 for (size_t i = 0; i < data->browsePathSize; ++i)
613 browsePath.push_back(t: scalarToQt<QOpcUaQualifiedName, UA_QualifiedName>(data: &data->browsePath[i]));
614
615 result.setBrowsePath(browsePath);
616
617 return result;
618}
619
620template<>
621QOpcUaLiteralOperand scalarToQt<QOpcUaLiteralOperand, UA_LiteralOperand>(const UA_LiteralOperand *data)
622{
623 QOpcUaLiteralOperand result;
624 result.setValue(toQVariant(value: data->value));
625 result.setType(toQtDataType(type: data->value.type));
626
627 return result;
628}
629
630template<>
631QOpcUaElementOperand scalarToQt<QOpcUaElementOperand, UA_ElementOperand>(const UA_ElementOperand *data)
632{
633 QOpcUaElementOperand result;
634 result.setIndex(data->index);
635
636 return result;
637}
638
639template<>
640QOpcUaRelativePathElement scalarToQt<QOpcUaRelativePathElement, UA_RelativePathElement>(const UA_RelativePathElement *data)
641{
642 QOpcUaRelativePathElement result;
643 result.setIncludeSubtypes(data->includeSubtypes);
644 result.setIsInverse(data->isInverse);
645 result.setReferenceTypeId(scalarToQt<QString, UA_NodeId>(data: &data->referenceTypeId));
646 result.setTargetName(scalarToQt<QOpcUaQualifiedName, UA_QualifiedName>(data: &data->targetName));
647
648 return result;
649}
650
651template<>
652QOpcUaAttributeOperand scalarToQt<QOpcUaAttributeOperand, UA_AttributeOperand>(const UA_AttributeOperand *data)
653{
654 QOpcUaAttributeOperand result;
655 result.setAttributeId(toQtAttributeId(attr: static_cast<UA_AttributeId>(data->attributeId)));
656 result.setNodeId(scalarToQt<QString, UA_NodeId>(data: &data->nodeId));
657 result.setAlias(scalarToQt<QString, UA_String>(data: &data->alias));
658 result.setIndexRange(scalarToQt<QString, UA_String>(data: &data->indexRange));
659
660 QList<QOpcUaRelativePathElement> browsePath;
661
662 for (size_t i = 0; i < data->browsePath.elementsSize; ++i)
663 browsePath.push_back(t: scalarToQt<QOpcUaRelativePathElement, UA_RelativePathElement>(data: &data->browsePath.elements[i]));
664
665 result.setBrowsePath(browsePath);
666
667 return result;
668}
669
670template<>
671QOpcUaContentFilterElement scalarToQt<QOpcUaContentFilterElement, UA_ContentFilterElement>(const UA_ContentFilterElement *data)
672{
673 QOpcUaContentFilterElement result;
674
675 result.setFilterOperator(QOpcUaContentFilterElement::FilterOperator(data->filterOperator));
676 QVariantList filterOperands;
677
678 for (size_t i = 0; i < data->filterOperandsSize; ++i) {
679 if (data->filterOperands[i].encoding < UA_EXTENSIONOBJECT_DECODED) {
680 filterOperands.push_back(t: scalarToQt<QOpcUaExtensionObject, UA_ExtensionObject>(data: &data->filterOperands[i]));
681 } else if (data->filterOperands[i].content.decoded.type == &UA_TYPES[UA_TYPES_LITERALOPERAND]) {
682 filterOperands.push_back(t: scalarToQt<QOpcUaLiteralOperand, UA_LiteralOperand>(
683 data: static_cast<UA_LiteralOperand *>(data->filterOperands[i].content.decoded.data)));
684 } else if (data->filterOperands[i].content.decoded.type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND]) {
685 filterOperands.push_back(t: scalarToQt<QOpcUaElementOperand, UA_ElementOperand>(
686 data: static_cast<UA_ElementOperand *>(data->filterOperands[i].content.decoded.data)));
687 } else if (data->filterOperands[i].content.decoded.type == &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND]) {
688 filterOperands.push_back(t: scalarToQt<QOpcUaAttributeOperand, UA_AttributeOperand>(
689 data: static_cast<UA_AttributeOperand *>(data->filterOperands[i].content.decoded.data)));
690 } else if (data->filterOperands[i].content.decoded.type == &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]) {
691 filterOperands.push_back(t: scalarToQt<QOpcUaSimpleAttributeOperand, UA_SimpleAttributeOperand>(
692 data: static_cast<UA_SimpleAttributeOperand *>(data->filterOperands[i].content.decoded.data)));
693 } else {
694 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Unknown operand in content filter element, unable to convert";
695 return {};
696 }
697 }
698
699 result.setFilterOperands(filterOperands);
700
701 return result;
702}
703
704template<>
705QOpcUaMonitoringParameters::EventFilter scalarToQt<QOpcUaMonitoringParameters::EventFilter, UA_EventFilter>(const UA_EventFilter *data)
706{
707 QOpcUaMonitoringParameters::EventFilter eventFilter;
708
709 for (size_t i = 0; i < data->selectClausesSize; ++i) {
710 eventFilter << scalarToQt<QOpcUaSimpleAttributeOperand, UA_SimpleAttributeOperand>(data: &data->selectClauses[i]);
711 }
712
713 for (size_t i = 0; i < data->whereClause.elementsSize; ++i) {
714 eventFilter << scalarToQt<QOpcUaContentFilterElement, UA_ContentFilterElement>(data: &data->whereClause.elements[i]);
715 }
716
717 return eventFilter;
718}
719
720template <>
721QVariant scalarToQt<QVariant, UA_ExtensionObject>(const UA_ExtensionObject *data)
722{
723 // OPC UA 1.05 part 6, 5.2.2.15 states that an extension object can have no body, a ByteString encoded body
724 // or an XML encoded body.
725
726 // Handle extension object without body
727 if (data->encoding == UA_EXTENSIONOBJECT_ENCODED_NOBODY) {
728 QOpcUaExtensionObject obj;
729 obj.setEncoding(QOpcUaExtensionObject::Encoding::NoBody);
730 return QVariant::fromValue(value: obj);
731 }
732
733 // Some types are automatically decoded by open62541. In this case, the encoding is UA_EXTENSIONOBJECT_DECODED
734 if (data->encoding != UA_EXTENSIONOBJECT_ENCODED_XML && data->encoding != UA_EXTENSIONOBJECT_ENCODED_BYTESTRING && data->content.decoded.data) {
735
736 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_ARGUMENT] && data->content.decoded.data != nullptr) {
737 return scalarToQt<QOpcUaArgument, UA_Argument>(data: reinterpret_cast<UA_Argument *>(data->content.decoded.data));
738 }
739
740 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_RANGE] && data->content.decoded.data != nullptr) {
741 return scalarToQt<QOpcUaRange, UA_Range>(data: reinterpret_cast<UA_Range *>(data->content.decoded.data));
742 }
743
744 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_EUINFORMATION] && data->content.decoded.data) {
745 return scalarToQt<QOpcUaEUInformation, UA_EUInformation>
746 (data: reinterpret_cast<UA_EUInformation *>(data->content.decoded.data));
747 }
748
749 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_COMPLEXNUMBERTYPE] && data->content.decoded.data) {
750 return scalarToQt<QOpcUaComplexNumber, UA_ComplexNumberType>
751 (data: reinterpret_cast<UA_ComplexNumberType *>(data->content.decoded.data));
752 }
753
754 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_DOUBLECOMPLEXNUMBERTYPE] && data->content.decoded.data) {
755 return scalarToQt<QOpcUaDoubleComplexNumber, UA_DoubleComplexNumberType>
756 (data: reinterpret_cast<UA_DoubleComplexNumberType *>(data->content.decoded.data));
757 }
758
759 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_AXISINFORMATION] && data->content.decoded.data) {
760 return scalarToQt<QOpcUaAxisInformation, UA_AxisInformation>
761 (data: reinterpret_cast<UA_AxisInformation *>(data->content.decoded.data));
762 }
763
764 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_XVTYPE] && data->content.decoded.data) {
765 return scalarToQt<QOpcUaXValue, UA_XVType>
766 (data: reinterpret_cast<UA_XVType *>(data->content.decoded.data));
767 }
768
769 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_STRUCTUREDEFINITION]) {
770 return scalarToQt<QOpcUaStructureDefinition, UA_StructureDefinition>
771 (data: reinterpret_cast<UA_StructureDefinition *>(data->content.decoded.data));
772 }
773
774 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_STRUCTUREFIELD]) {
775 return scalarToQt<QOpcUaStructureField, UA_StructureField>
776 (data: reinterpret_cast<UA_StructureField *>(data->content.decoded.data));
777 }
778
779 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_ENUMDEFINITION]) {
780 return scalarToQt<QOpcUaEnumDefinition, UA_EnumDefinition>
781 (data: reinterpret_cast<UA_EnumDefinition *>(data->content.decoded.data));
782 }
783
784 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_ENUMFIELD]) {
785 return scalarToQt<QOpcUaEnumField, UA_EnumField>
786 (data: reinterpret_cast<UA_EnumField *>(data->content.decoded.data));
787 }
788
789 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND] && data->content.decoded.data) {
790 return scalarToQt<QOpcUaSimpleAttributeOperand, UA_SimpleAttributeOperand>
791 (data: reinterpret_cast<UA_SimpleAttributeOperand *>(data->content.decoded.data));
792 }
793
794 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND] && data->content.decoded.data) {
795 return scalarToQt<QOpcUaAttributeOperand, UA_AttributeOperand>
796 (data: reinterpret_cast<UA_AttributeOperand *>(data->content.decoded.data));
797 }
798
799 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_LITERALOPERAND] && data->content.decoded.data) {
800 return scalarToQt<QOpcUaLiteralOperand, UA_LiteralOperand>
801 (data: reinterpret_cast<UA_LiteralOperand *>(data->content.decoded.data));
802 }
803
804 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND] && data->content.decoded.data) {
805 return scalarToQt<QOpcUaElementOperand, UA_ElementOperand>
806 (data: reinterpret_cast<UA_ElementOperand *>(data->content.decoded.data));
807 }
808
809 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT] && data->content.decoded.data) {
810 return scalarToQt<QOpcUaRelativePathElement, UA_RelativePathElement>
811 (data: reinterpret_cast<UA_RelativePathElement *>(data->content.decoded.data));
812 }
813
814 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT] && data->content.decoded.data) {
815 return scalarToQt<QOpcUaContentFilterElement, UA_ContentFilterElement>
816 (data: reinterpret_cast<UA_ContentFilterElement *>(data->content.decoded.data));
817 }
818
819 if (data->content.decoded.type == &UA_TYPES[UA_TYPES_EVENTFILTER] && data->content.decoded.data) {
820 return scalarToQt<QOpcUaMonitoringParameters::EventFilter, UA_EventFilter>
821 (data: reinterpret_cast<UA_EventFilter *>(data->content.decoded.data));
822 }
823
824 bool success = false;
825 const auto result = encodeAsBinaryExtensionObject(data: data->content.decoded.data, type: data->content.decoded.type, success: &success);
826 if (success)
827 return result;
828
829 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to re-encode decoded extension object, unable to convert" << data->content.decoded.type->typeName;
830 return {};
831 }
832
833 QByteArray buffer = QByteArray::fromRawData(data: reinterpret_cast<const char *>(data->content.encoded.body.data),
834 size: data->content.encoded.body.length);
835
836 // Return extension objects with binary or XML body as QOpcUaExtensionObject
837 QOpcUaExtensionObject obj;
838 obj.setEncoding(static_cast<QOpcUaExtensionObject::Encoding>(data->encoding));
839 obj.setEncodingTypeId(Open62541Utils::nodeIdToQString(id: data->content.encoded.typeId));
840 obj.setEncodedBody(QByteArray(buffer.constData(), buffer.size()));
841 return obj;
842}
843
844template<>
845QOpcUaExpandedNodeId scalarToQt<QOpcUaExpandedNodeId, UA_ExpandedNodeId>(const UA_ExpandedNodeId *data)
846{
847 QOpcUaExpandedNodeId temp;
848 temp.setServerIndex(data->serverIndex);
849 temp.setNodeId(Open62541Utils::nodeIdToQString(id: data->nodeId));
850 temp.setNamespaceUri(scalarToQt<QString, UA_String>(data: &data->namespaceUri));
851 return temp;
852}
853
854template<typename TARGETTYPE, typename UATYPE>
855QVariant arrayToQVariant(const UA_Variant &var, QMetaType::Type type)
856{
857 UATYPE *temp = static_cast<UATYPE *>(var.data);
858
859 if (var.arrayLength > 0) {
860 QVariantList list(var.arrayLength);
861 for (size_t i = 0; i < var.arrayLength; ++i) {
862 QVariant tempVar = QVariant::fromValue(scalarToQt<TARGETTYPE, UATYPE>(&temp[i]));
863 if (type != QMetaType::UnknownType && type != static_cast<QMetaType::Type>(tempVar.metaType().id()))
864 tempVar.convert(type: QMetaType(type));
865 list[i] = tempVar;
866 }
867
868 if (var.arrayDimensionsSize > 0) {
869 // Ensure that the array dimensions fit in a QList
870 if (var.arrayDimensionsSize > static_cast<quint64>((std::numeric_limits<int>::max)()))
871 return QOpcUaMultiDimensionalArray();
872 QList<quint32> arrayDimensions;
873 std::copy(first: var.arrayDimensions, last: var.arrayDimensions+var.arrayDimensionsSize, result: std::back_inserter(x&: arrayDimensions));
874 return QOpcUaMultiDimensionalArray(list, arrayDimensions);
875 }
876
877 if (list.size() == 1)
878 return list.at(i: 0);
879 else
880 return list;
881 } else if (UA_Variant_isScalar(v: &var)) {
882 QVariant tempVar = QVariant::fromValue(scalarToQt<TARGETTYPE, UATYPE>(temp));
883 if (type != QMetaType::UnknownType && type != static_cast<QMetaType::Type>(tempVar.metaType().id()))
884 tempVar.convert(type: QMetaType(type));
885 return tempVar;
886 } else if (var.arrayLength == 0 && var.data == UA_EMPTY_ARRAY_SENTINEL) {
887 return QVariantList(); // Return empty QVariantList for empty array
888 }
889
890 return QVariant(); // Return empty QVariant for empty scalar variant
891}
892
893template<typename TARGETTYPE, typename QTTYPE>
894void scalarFromQt(const QTTYPE &value, TARGETTYPE *ptr)
895{
896 *ptr = static_cast<TARGETTYPE>(value);
897}
898
899template<>
900void scalarFromQt<UA_DateTime, QDateTime>(const QDateTime &value, UA_DateTime *ptr)
901{
902 if (!value.isValid()) {
903 *ptr = (std::numeric_limits<qint64>::min)();
904 return;
905 }
906
907 // OPC UA 1.05 part 6, 5.1.4
908 const QDateTime uaEpochStart(QDate(1601, 1, 1), QTime(0, 0), QTimeZone::UTC);
909
910 *ptr = UA_DATETIME_MSEC * (value.toMSecsSinceEpoch() - uaEpochStart.toMSecsSinceEpoch());
911}
912
913template<>
914void scalarFromQt<UA_String, QString>(const QString &value, UA_String *ptr)
915{
916 *ptr = UA_STRING_ALLOC(value.toUtf8().constData());
917}
918
919template<>
920void scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(const QOpcUaLocalizedText &value, UA_LocalizedText *ptr)
921{
922 scalarFromQt<UA_String, QString>(value: value.locale(), ptr: &(ptr->locale));
923 scalarFromQt<UA_String, QString>(value: value.text(), ptr: &(ptr->text));
924}
925
926template<>
927void scalarFromQt<UA_ByteString, QByteArray>(const QByteArray &value, UA_ByteString *ptr)
928{
929 ptr->length = value.size();
930 UA_StatusCode success = UA_Array_copy(src: reinterpret_cast<const UA_Byte *>(value.constData()),
931 size: value.size(), dst: reinterpret_cast<void **>(&ptr->data), type: &UA_TYPES[UA_TYPES_BYTE]);
932 if (success != UA_STATUSCODE_GOOD) {
933 ptr->length = 0;
934 ptr->data = nullptr;
935 }
936}
937
938template<>
939void scalarFromQt<UA_NodeId, QString>(const QString &value, UA_NodeId *ptr)
940{
941 *ptr = Open62541Utils::nodeIdFromQString(name: value);
942}
943
944template<>
945void scalarFromQt<UA_QualifiedName, QOpcUaQualifiedName>(const QOpcUaQualifiedName &value, UA_QualifiedName *ptr)
946{
947 ptr->namespaceIndex = value.namespaceIndex();
948 scalarFromQt<UA_String, QString>(value: value.name(), ptr: &(ptr->name));
949}
950
951template<>
952void scalarFromQt<UA_Guid, QUuid>(const QUuid &value, UA_Guid *ptr)
953{
954 ptr->data1 = value.data1;
955 ptr->data2 = value.data2;
956 ptr->data3 = value.data3;
957 std::memcpy(dest: ptr->data4, src: value.data4, n: sizeof(value.data4));
958}
959
960template<>
961void scalarFromQt<UA_Range, QOpcUaRange>(const QOpcUaRange &value, UA_Range *ptr)
962{
963 ptr->low = value.low();
964 ptr->high = value.high();
965}
966
967template<>
968void scalarFromQt<UA_EUInformation, QOpcUaEUInformation>(const QOpcUaEUInformation &value, UA_EUInformation *ptr)
969{
970 scalarFromQt<UA_String, QString>(value: value.namespaceUri(), ptr: &ptr->namespaceUri);
971 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.description(), ptr: &ptr->description);
972 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.displayName(), ptr: &ptr->displayName);
973 ptr->unitId = value.unitId();
974}
975
976template<>
977void scalarFromQt<UA_ComplexNumberType, QOpcUaComplexNumber>(const QOpcUaComplexNumber &value, UA_ComplexNumberType *ptr)
978{
979 ptr->real = value.real();
980 ptr->imaginary = value.imaginary();
981}
982
983template<>
984void scalarFromQt<UA_DoubleComplexNumberType, QOpcUaDoubleComplexNumber>(const QOpcUaDoubleComplexNumber &value, UA_DoubleComplexNumberType *ptr)
985{
986 ptr->real = value.real();
987 ptr->imaginary = value.imaginary();
988}
989
990template<>
991void scalarFromQt<UA_AxisInformation, QOpcUaAxisInformation>(const QOpcUaAxisInformation &value, UA_AxisInformation *ptr)
992{
993 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.title(), ptr: &ptr->title);
994 scalarFromQt<UA_EUInformation, QOpcUaEUInformation>(value: value.engineeringUnits(), ptr: &ptr->engineeringUnits);
995 scalarFromQt<UA_Range, QOpcUaRange>(value: value.eURange(), ptr: &ptr->eURange);
996 ptr->axisScaleType = static_cast<UA_AxisScaleEnumeration>(value.axisScaleType());
997 ptr->axisStepsSize = value.axisSteps().size();
998 if (ptr->axisStepsSize) {
999 auto res = UA_Array_copy(src: value.axisSteps().constData(), size: ptr->axisStepsSize, dst: reinterpret_cast<void **>(&ptr->axisSteps),
1000 type: &UA_TYPES[UA_TYPES_DOUBLE]);
1001
1002 if (res != UA_STATUSCODE_GOOD)
1003 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to copy axis steps";
1004 } else {
1005 ptr->axisSteps = nullptr;
1006 }
1007}
1008
1009template<>
1010void scalarFromQt<UA_XVType, QOpcUaXValue>(const QOpcUaXValue &value, UA_XVType *ptr)
1011{
1012 ptr->x = value.x();
1013 ptr->value = value.value();
1014}
1015
1016template<>
1017void scalarFromQt<UA_StructureField, QOpcUaStructureField>(const QOpcUaStructureField &value, UA_StructureField *ptr)
1018{
1019 scalarFromQt<UA_NodeId, QString>(value: value.dataType(), ptr: &ptr->dataType);
1020 ptr->maxStringLength = value.maxStringLength();
1021 ptr->isOptional = value.isOptional();
1022 ptr->valueRank = value.valueRank();
1023 scalarFromQt<UA_String, QString>(value: value.name(), ptr: &ptr->name);
1024 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.description(), ptr: &ptr->description);
1025
1026 ptr->arrayDimensionsSize = value.arrayDimensions().size();
1027 if (ptr->arrayDimensionsSize) {
1028 ptr->arrayDimensions = static_cast<quint32 *>(UA_Array_new(size: ptr->arrayDimensionsSize, type: &UA_TYPES[UA_TYPES_UINT32]));
1029
1030 for (int i = 0; i < value.arrayDimensions().size(); ++i)
1031 ptr->arrayDimensions[i] = value.arrayDimensions().at(i);
1032 }
1033}
1034
1035template<>
1036void scalarFromQt<UA_StructureDefinition, QOpcUaStructureDefinition>(const QOpcUaStructureDefinition &value, UA_StructureDefinition *ptr)
1037{
1038 scalarFromQt<UA_NodeId, QString>(value: value.baseDataType(), ptr: &ptr->baseDataType);
1039 scalarFromQt<UA_NodeId, QString>(value: value.defaultEncodingId(), ptr: &ptr->defaultEncodingId);
1040 ptr->structureType = static_cast<UA_StructureType>(value.structureType());
1041
1042 if (!value.fields().isEmpty()) {
1043 ptr->fieldsSize = value.fields().size();
1044 ptr->fields = static_cast<UA_StructureField *>(UA_Array_new(size: ptr->fieldsSize, type: &UA_TYPES[UA_TYPES_STRUCTUREFIELD]));
1045
1046 for (int i = 0; i < value.fields().size(); ++i)
1047 scalarFromQt<UA_StructureField, QOpcUaStructureField>(value: value.fields().at(i), ptr: &ptr->fields[i]);
1048 }
1049}
1050
1051template<>
1052void scalarFromQt<UA_EnumField, QOpcUaEnumField>(const QOpcUaEnumField &value, UA_EnumField *ptr)
1053{
1054 scalarFromQt<UA_String, QString>(value: value.name(), ptr: &ptr->name);
1055 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.description(), ptr: &ptr->description);
1056 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.displayName(), ptr: &ptr->displayName);
1057 ptr->value = value.value();
1058}
1059
1060template<>
1061void scalarFromQt<UA_EnumDefinition, QOpcUaEnumDefinition>(const QOpcUaEnumDefinition &value, UA_EnumDefinition *ptr)
1062{
1063 if (!value.fields().isEmpty()) {
1064 ptr->fieldsSize = value.fields().size();
1065 ptr->fields = static_cast<UA_EnumField *>(UA_Array_new(size: ptr->fieldsSize, type: &UA_TYPES[UA_TYPES_ENUMFIELD]));
1066
1067 for (int i = 0; i < value.fields().size(); ++i)
1068 scalarFromQt<UA_EnumField, QOpcUaEnumField>(value: value.fields().at(i), ptr: &ptr->fields[i]);
1069 }
1070}
1071
1072template<>
1073void scalarFromQt<UA_DiagnosticInfo, QOpcUaDiagnosticInfo>(const QOpcUaDiagnosticInfo &value, UA_DiagnosticInfo *ptr)
1074{
1075 if (value.hasSymbolicId()) {
1076 ptr->hasSymbolicId = true;
1077 ptr->symbolicId = value.symbolicId();
1078 }
1079
1080 if (value.hasNamespaceUri()) {
1081 ptr->hasNamespaceUri = true;
1082 ptr->namespaceUri = value.namespaceUri();
1083 }
1084
1085 if (value.hasLocale()) {
1086 ptr->hasLocale = true;
1087 ptr->locale = value.locale();
1088 }
1089
1090 if (value.hasLocalizedText()) {
1091 ptr->hasLocalizedText = true;
1092 ptr->localizedText = value.localizedText();
1093 }
1094
1095 if (value.hasAdditionalInfo()) {
1096 ptr->hasAdditionalInfo = true;
1097 scalarFromQt<UA_String, QString>(value: value.additionalInfo(), ptr: &ptr->additionalInfo);
1098 }
1099
1100 if (value.hasInnerStatusCode()) {
1101 ptr->hasInnerStatusCode = true;
1102 ptr->innerStatusCode = value.innerStatusCode();
1103 }
1104
1105 if (value.hasInnerDiagnosticInfo()) {
1106 ptr->hasInnerDiagnosticInfo = true;
1107 ptr->innerDiagnosticInfo = UA_DiagnosticInfo_new();
1108 scalarFromQt<UA_DiagnosticInfo, QOpcUaDiagnosticInfo>(value: value.innerDiagnosticInfo(), ptr: ptr->innerDiagnosticInfo);
1109 }
1110}
1111
1112template<>
1113void scalarFromQt<UA_ExtensionObject, QOpcUaExtensionObject>(const QOpcUaExtensionObject &obj, UA_ExtensionObject *ptr)
1114{
1115 QByteArray temp = obj.encodedBody();
1116 UA_NodeId encodingId = Open62541Utils::nodeIdFromQString(name: obj.encodingTypeId());
1117 UaDeleter<UA_NodeId> nodeIdDeleter(&encodingId, UA_NodeId_clear);
1118 createExtensionObject(data&: temp, typeEncodingId: encodingId, ptr, encoding: obj.encoding());
1119}
1120
1121template<>
1122void scalarFromQt<UA_ExpandedNodeId, QOpcUaExpandedNodeId>(const QOpcUaExpandedNodeId &value, UA_ExpandedNodeId *ptr)
1123{
1124 ptr->serverIndex = value.serverIndex();
1125 scalarFromQt<UA_String, QString>(value: value.namespaceUri(), ptr: &ptr->namespaceUri);
1126 ptr->nodeId = Open62541Utils::nodeIdFromQString(name: value.nodeId());
1127}
1128
1129template<>
1130void scalarFromQt<UA_Argument, QOpcUaArgument>(const QOpcUaArgument &value, UA_Argument *ptr)
1131{
1132 ptr->valueRank = value.valueRank();
1133 scalarFromQt<UA_LocalizedText, QOpcUaLocalizedText>(value: value.description(), ptr: &ptr->description);
1134 scalarFromQt<UA_String, QString>(value: value.name(), ptr: &ptr->name);
1135 ptr->dataType = Open62541Utils::nodeIdFromQString(name: value.dataTypeId());
1136 ptr->arrayDimensionsSize = value.arrayDimensions().size();
1137 UA_StatusCode res = UA_Array_copy(src: value.arrayDimensions().constData(), size: ptr->arrayDimensionsSize,
1138 dst: reinterpret_cast<void **>(&ptr->arrayDimensions), type: &UA_TYPES[UA_TYPES_UINT32]);
1139 if (res != UA_STATUSCODE_GOOD)
1140 ptr->arrayDimensionsSize = 0;
1141}
1142
1143template<>
1144void scalarFromQt<UA_SimpleAttributeOperand, QOpcUaSimpleAttributeOperand>(const QOpcUaSimpleAttributeOperand &value, UA_SimpleAttributeOperand *ptr)
1145{
1146 ptr->attributeId = toUaAttributeId(attr: value.attributeId());
1147 if (!value.indexRange().isEmpty())
1148 scalarFromQt<UA_String, QString>(value: value.indexRange(), ptr: &ptr->indexRange);
1149 scalarFromQt<UA_NodeId, QString>(value: value.typeId(), ptr: &ptr->typeDefinitionId);
1150 ptr->browsePathSize = value.browsePath().size();
1151 if (!value.browsePath().isEmpty()) {
1152 ptr->browsePath = static_cast<UA_QualifiedName *>(UA_Array_new(size: value.browsePath().size(), type: &UA_TYPES[UA_TYPES_QUALIFIEDNAME]));
1153 for (size_t i = 0; i < ptr->browsePathSize; ++i)
1154 scalarFromQt<UA_QualifiedName, QOpcUaQualifiedName>(value: value.browsePath().at(i), ptr: &ptr->browsePath[i]);
1155 } else {
1156 ptr->browsePath = static_cast<UA_QualifiedName *>(UA_EMPTY_ARRAY_SENTINEL);
1157 }
1158}
1159
1160template<>
1161void scalarFromQt<UA_LiteralOperand, QOpcUaLiteralOperand>(const QOpcUaLiteralOperand &value, UA_LiteralOperand *ptr)
1162{
1163 ptr->value = toOpen62541Variant(value: value.value(), type: value.type());
1164}
1165
1166template<>
1167void scalarFromQt<UA_ElementOperand, QOpcUaElementOperand>(const QOpcUaElementOperand &value, UA_ElementOperand *ptr)
1168{
1169 ptr->index = value.index();
1170}
1171
1172template<>
1173void scalarFromQt<UA_RelativePathElement, QOpcUaRelativePathElement>(const QOpcUaRelativePathElement &value, UA_RelativePathElement *ptr)
1174{
1175 ptr->includeSubtypes = value.includeSubtypes();
1176 ptr->isInverse = value.isInverse();
1177 scalarFromQt<UA_NodeId, QString>(value: value.referenceTypeId(), ptr: &ptr->referenceTypeId);
1178 scalarFromQt<UA_QualifiedName, QOpcUaQualifiedName>(value: value.targetName(), ptr: &ptr->targetName);
1179}
1180
1181template<>
1182void scalarFromQt<UA_AttributeOperand, QOpcUaAttributeOperand>(const QOpcUaAttributeOperand &value, UA_AttributeOperand *ptr)
1183{
1184 ptr->attributeId = toUaAttributeId(attr: value.attributeId());
1185 scalarFromQt<UA_String, QString>(value: value.alias(), ptr: &ptr->alias);
1186 if (!value.indexRange().isEmpty())
1187 scalarFromQt<UA_String, QString>(value: value.indexRange(), ptr: &ptr->indexRange);
1188 scalarFromQt<UA_NodeId, QString>(value: value.nodeId(), ptr: &ptr->nodeId);
1189 ptr->browsePath.elementsSize = value.browsePath().size();
1190 if (ptr->browsePath.elementsSize) {
1191 ptr->browsePath.elements = static_cast<UA_RelativePathElement *>(UA_Array_new(size: ptr->browsePath.elementsSize,
1192 type: &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT]));
1193 for (size_t i = 0; i < ptr->browsePath.elementsSize; ++i)
1194 scalarFromQt<UA_RelativePathElement, QOpcUaRelativePathElement>(value: value.browsePath().at(i),
1195 ptr: &ptr->browsePath.elements[i]);
1196 }
1197}
1198
1199template<>
1200void scalarFromQt<UA_ContentFilterElement, QOpcUaContentFilterElement>(const QOpcUaContentFilterElement &value, UA_ContentFilterElement *ptr)
1201{
1202 ptr->filterOperator = static_cast<UA_FilterOperator>(value.filterOperator());
1203 ptr->filterOperandsSize = value.filterOperands().size();
1204 if (ptr->filterOperandsSize) {
1205 ptr->filterOperands = static_cast<UA_ExtensionObject *>(UA_Array_new(size: ptr->filterOperandsSize, type: &UA_TYPES[UA_TYPES_EXTENSIONOBJECT]));
1206
1207 for (size_t i = 0; i < ptr->filterOperandsSize; ++i) {
1208 if (value.filterOperands().at(i).canConvert<QOpcUaElementOperand>()) {
1209 const auto operand = UA_ElementOperand_new();
1210 scalarFromQt<UA_ElementOperand, QOpcUaElementOperand>(value: value.filterOperands().at(i).value<QOpcUaElementOperand>(), ptr: operand);
1211 ptr->filterOperands[i].encoding = UA_EXTENSIONOBJECT_DECODED;
1212 ptr->filterOperands[i].content.decoded.data = operand;
1213 ptr->filterOperands[i].content.decoded.type = &UA_TYPES[UA_TYPES_ELEMENTOPERAND];
1214 } else if (value.filterOperands().at(i).canConvert<QOpcUaLiteralOperand>()) {
1215 const auto operand = UA_LiteralOperand_new();
1216 scalarFromQt<UA_LiteralOperand, QOpcUaLiteralOperand>(value: value.filterOperands().at(i).value<QOpcUaLiteralOperand>(), ptr: operand);
1217 ptr->filterOperands[i].encoding = UA_EXTENSIONOBJECT_DECODED;
1218 ptr->filterOperands[i].content.decoded.data = operand;
1219 ptr->filterOperands[i].content.decoded.type = &UA_TYPES[UA_TYPES_LITERALOPERAND];
1220 } else if (value.filterOperands().at(i).canConvert<QOpcUaAttributeOperand>()) {
1221 const auto operand = UA_AttributeOperand_new();
1222 scalarFromQt<UA_AttributeOperand, QOpcUaAttributeOperand>(value: value.filterOperands().at(i).value<QOpcUaAttributeOperand>(), ptr: operand);
1223 ptr->filterOperands[i].encoding = UA_EXTENSIONOBJECT_DECODED;
1224 ptr->filterOperands[i].content.decoded.data = operand;
1225 ptr->filterOperands[i].content.decoded.type = &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND];
1226 } else if (value.filterOperands().at(i).canConvert<QOpcUaSimpleAttributeOperand>()) {
1227 const auto operand = UA_SimpleAttributeOperand_new();
1228 scalarFromQt<UA_SimpleAttributeOperand, QOpcUaSimpleAttributeOperand>(value: value.filterOperands().at(i).value<QOpcUaSimpleAttributeOperand>(), ptr: operand);
1229 ptr->filterOperands[i].encoding = UA_EXTENSIONOBJECT_DECODED;
1230 ptr->filterOperands[i].content.decoded.data = operand;
1231 ptr->filterOperands[i].content.decoded.type = &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND];
1232 } else {
1233 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Unsupported operand type in content filter element, unable to convert";
1234 UA_ContentFilterElement_clear(p: ptr);
1235 return;
1236 }
1237 }
1238 }
1239}
1240
1241template<>
1242void scalarFromQt<UA_EventFilter, QOpcUaMonitoringParameters::EventFilter>(const QOpcUaMonitoringParameters::EventFilter &value, UA_EventFilter *ptr)
1243{
1244 ptr->selectClausesSize = value.selectClauses().size();
1245 if (ptr->selectClausesSize) {
1246 ptr->selectClauses = static_cast<UA_SimpleAttributeOperand *>(
1247 UA_Array_new(size: ptr->selectClausesSize, type: &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND]));
1248
1249 for (size_t i = 0; i < ptr->selectClausesSize; ++i)
1250 scalarFromQt<UA_SimpleAttributeOperand, QOpcUaSimpleAttributeOperand>(value: value.selectClauses().at(i),
1251 ptr: &ptr->selectClauses[i]);
1252 }
1253
1254 ptr->whereClause.elementsSize = value.whereClause().size();
1255 if (ptr->whereClause.elementsSize) {
1256 ptr->whereClause.elements = static_cast<UA_ContentFilterElement *>(
1257 UA_Array_new(size: ptr->whereClause.elementsSize, type: &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT]));
1258
1259 for (size_t i = 0; i < ptr->whereClause.elementsSize; ++i)
1260 scalarFromQt<UA_ContentFilterElement, QOpcUaContentFilterElement>(value: value.whereClause().at(i),
1261 ptr: &ptr->whereClause.elements[i]);
1262 }
1263}
1264
1265template<typename TARGETTYPE, typename QTTYPE>
1266UA_Variant arrayFromQVariant(const QVariant &var, const UA_DataType *type)
1267{
1268 UA_Variant open62541value;
1269 UA_Variant_init(p: &open62541value);
1270
1271 if (type == nullptr) {
1272 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Unable to convert QVariant to UA_Variant, unknown type";
1273 return open62541value;
1274 }
1275
1276 if (var.metaType().id() == QMetaType::QVariantList) {
1277 const QVariantList list = var.toList();
1278 if (list.isEmpty())
1279 return open62541value;
1280
1281 for (const auto &it : std::as_const(t: list)) {
1282 if (!it.canConvert<QTTYPE>()) {
1283 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Value type" << var.typeName() <<
1284 "in the QVariant does not match type parameter" << type->typeName;
1285 return open62541value;
1286 }
1287 }
1288
1289 TARGETTYPE *arr = static_cast<TARGETTYPE *>(UA_Array_new(size: list.size(), type));
1290
1291 for (int i = 0; i < list.size(); ++i)
1292 scalarFromQt<TARGETTYPE, QTTYPE>(list[i].value<QTTYPE>(), &arr[i]);
1293
1294 UA_Variant_setArray(&open62541value, arr, list.size(), type);
1295 return open62541value;
1296 }
1297
1298 if (!var.canConvert<QTTYPE>()) {
1299 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Value type" << var.typeName() <<
1300 "in the QVariant does not match type parameter" << type->typeName;
1301 return open62541value;
1302 }
1303
1304 TARGETTYPE *temp = static_cast<TARGETTYPE *>(UA_new(type));
1305 scalarFromQt<TARGETTYPE, QTTYPE>(var.value<QTTYPE>(), temp);
1306 UA_Variant_setScalar(&open62541value, temp, type);
1307 return open62541value;
1308}
1309
1310void createExtensionObject(QByteArray &data, const UA_NodeId &typeEncodingId, UA_ExtensionObject *ptr, QOpcUaExtensionObject::Encoding encoding)
1311{
1312 UA_ExtensionObject obj;
1313 UA_ExtensionObject_init(p: &obj);
1314
1315 if (!data.isEmpty() && encoding == QOpcUaExtensionObject::Encoding::NoBody)
1316 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Data for extension object provided but will not be encoded because encoding format is set to skip the body";
1317
1318 if (encoding != QOpcUaExtensionObject::Encoding::NoBody) {
1319 obj.encoding = static_cast<UA_ExtensionObjectEncoding>(encoding);
1320 obj.content.encoded.body.data = reinterpret_cast<UA_Byte *>(data.data());
1321 obj.content.encoded.body.length = data.size();
1322 }
1323 obj.content.encoded.typeId = typeEncodingId;
1324 UA_ExtensionObject_copy(src: &obj, dst: ptr);
1325}
1326
1327QVariant uaVariantToQtExtensionObject(const UA_Variant &var)
1328{
1329 if (UA_Variant_isScalar(v: &var)) {
1330 bool success = false;
1331 const auto result = encodeAsBinaryExtensionObject(data: var.data, type: var.type, success: &success);
1332 if (success)
1333 return result;
1334
1335 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Failed to re-encode decoded extension object, unable to convert" << var.type->typeName;
1336 return {};
1337 } else if (var.arrayLength == 0 && var.data == UA_EMPTY_ARRAY_SENTINEL) {
1338 return QVariantList(); // Return empty QVariantList for empty array
1339 } else if (var.arrayLength > 0) {
1340 QVariantList values;
1341 values.reserve(asize: var.arrayLength);
1342 for (size_t i = 0; i < var.arrayLength; ++i) {
1343 bool success = false;
1344 values.push_back(t: encodeAsBinaryExtensionObject(data: static_cast<quint8 *>(var.data) + (i * var.type->memSize), type: var.type, success: &success));
1345
1346 if (!success)
1347 return {};
1348 }
1349
1350 if (var.arrayDimensionsSize > 0) {
1351 QOpcUaMultiDimensionalArray arr;
1352 arr.setValueArray(values);
1353 QList<quint32> arrayDimensions(var.arrayDimensionsSize);
1354 for (size_t i = 0; i < var.arrayDimensionsSize; ++i)
1355 arrayDimensions[i] = var.arrayDimensions[i];
1356 arr.setArrayDimensions(arrayDimensions);
1357 return arr;
1358 }
1359
1360 return values;
1361 }
1362
1363 return QVariant();
1364}
1365
1366QOpcUaExtensionObject encodeAsBinaryExtensionObject(const void *data, const UA_DataType *type, bool *success)
1367{
1368 if (!data || !type) {
1369 if (success)
1370 *success = false;
1371 return {};
1372 }
1373
1374 UA_ByteString encodedData;
1375 UA_ByteString_init(p: &encodedData);
1376 const auto encodeResult = UA_encodeBinary(p: data, type, outBuf: &encodedData);
1377
1378 if (encodeResult != UA_STATUSCODE_GOOD) {
1379 if (success)
1380 *success = false;
1381
1382 return {};
1383 }
1384
1385 QOpcUaExtensionObject result;
1386 result.setBinaryEncodedBody(encodedBody: scalarToQt<QByteArray, UA_ByteString>(data: &encodedData), typeId: scalarToQt<QString, UA_NodeId>(data: &type->typeId));
1387 UA_ByteString_clear(p: &encodedData);
1388 result.setEncodingTypeId(scalarToQt<QString, UA_NodeId>(data: &type->binaryEncodingId));
1389
1390 if (success)
1391 *success = true;
1392
1393 return result;
1394}
1395
1396QOpcUa::Types toQtDataType(const UA_DataType *type)
1397{
1398 if (type == &UA_TYPES[UA_TYPES_BOOLEAN])
1399 return QOpcUa::Types::Boolean;
1400 if (type == &UA_TYPES[UA_TYPES_INT32])
1401 return QOpcUa::Types::Int32;
1402 if (type == &UA_TYPES[UA_TYPES_UINT32])
1403 return QOpcUa::Types::UInt32;
1404 if (type == &UA_TYPES[UA_TYPES_DOUBLE])
1405 return QOpcUa::Types::Double;
1406 if (type == &UA_TYPES[UA_TYPES_FLOAT])
1407 return QOpcUa::Types::Float;
1408 if (type == &UA_TYPES[UA_TYPES_STRING])
1409 return QOpcUa::Types::String;
1410 if (type == &UA_TYPES[UA_TYPES_LOCALIZEDTEXT])
1411 return QOpcUa::Types::LocalizedText;
1412 if (type == &UA_TYPES[UA_TYPES_DATETIME])
1413 return QOpcUa::Types::DateTime;
1414 if (type == &UA_TYPES[UA_TYPES_UINT16])
1415 return QOpcUa::Types::UInt16;
1416 if (type == &UA_TYPES[UA_TYPES_INT16])
1417 return QOpcUa::Types::Int16;
1418 if (type == &UA_TYPES[UA_TYPES_UINT64])
1419 return QOpcUa::Types::UInt64;
1420 if (type == &UA_TYPES[UA_TYPES_INT64])
1421 return QOpcUa::Types::Int64;
1422 if (type == &UA_TYPES[UA_TYPES_BYTE])
1423 return QOpcUa::Types::Byte;
1424 if (type == &UA_TYPES[UA_TYPES_SBYTE])
1425 return QOpcUa::Types::SByte;
1426 if (type == &UA_TYPES[UA_TYPES_BYTESTRING])
1427 return QOpcUa::Types::ByteString;
1428 if (type == &UA_TYPES[UA_TYPES_XMLELEMENT])
1429 return QOpcUa::Types::XmlElement;
1430 if (type == &UA_TYPES[UA_TYPES_NODEID])
1431 return QOpcUa::Types::NodeId;
1432 if (type == &UA_TYPES[UA_TYPES_GUID])
1433 return QOpcUa::Types::Guid;
1434 if (type == &UA_TYPES[UA_TYPES_QUALIFIEDNAME])
1435 return QOpcUa::Types::QualifiedName;
1436 if (type == &UA_TYPES[UA_TYPES_STATUSCODE])
1437 return QOpcUa::Types::StatusCode;
1438 if (type == &UA_TYPES[UA_TYPES_RANGE])
1439 return QOpcUa::Types::Range;
1440 if (type == &UA_TYPES[UA_TYPES_EUINFORMATION])
1441 return QOpcUa::Types::EUInformation;
1442 if (type == &UA_TYPES[UA_TYPES_COMPLEXNUMBERTYPE])
1443 return QOpcUa::Types::ComplexNumber;
1444 if (type == &UA_TYPES[UA_TYPES_DOUBLECOMPLEXNUMBERTYPE])
1445 return QOpcUa::Types::DoubleComplexNumber;
1446 if (type == &UA_TYPES[UA_TYPES_AXISINFORMATION])
1447 return QOpcUa::Types::AxisInformation;
1448 if (type == &UA_TYPES[UA_TYPES_XVTYPE])
1449 return QOpcUa::Types::XV;
1450 if (type == &UA_TYPES[UA_TYPES_EXTENSIONOBJECT])
1451 return QOpcUa::Types::ExtensionObject;
1452 if (type == &UA_TYPES[UA_TYPES_EXPANDEDNODEID])
1453 return QOpcUa::Types::ExpandedNodeId;
1454 if (type == &UA_TYPES[UA_TYPES_ARGUMENT])
1455 return QOpcUa::Types::Argument;
1456 if (type == &UA_TYPES[UA_TYPES_STRUCTUREDEFINITION])
1457 return QOpcUa::Types::StructureDefinition;
1458 if (type == &UA_TYPES[UA_TYPES_STRUCTUREFIELD])
1459 return QOpcUa::Types::StructureField;
1460 if (type == &UA_TYPES[UA_TYPES_ENUMDEFINITION])
1461 return QOpcUa::Types::EnumDefinition;
1462 if (type == &UA_TYPES[UA_TYPES_ENUMFIELD])
1463 return QOpcUa::Types::EnumField;
1464 if (type == &UA_TYPES[UA_TYPES_DIAGNOSTICINFO])
1465 return QOpcUa::Types::DiagnosticInfo;
1466 if (type == &UA_TYPES[UA_TYPES_SIMPLEATTRIBUTEOPERAND])
1467 return QOpcUa::Types::SimpleAttributeOperand;
1468 if (type == &UA_TYPES[UA_TYPES_ATTRIBUTEOPERAND])
1469 return QOpcUa::Types::AttributeOperand;
1470 if (type == &UA_TYPES[UA_TYPES_LITERALOPERAND])
1471 return QOpcUa::Types::LiteralOperand;
1472 if (type == &UA_TYPES[UA_TYPES_ELEMENTOPERAND])
1473 return QOpcUa::Types::ElementOperand;
1474 if (type == &UA_TYPES[UA_TYPES_RELATIVEPATHELEMENT])
1475 return QOpcUa::Types::RelativePathElement;
1476 if (type == &UA_TYPES[UA_TYPES_CONTENTFILTERELEMENT])
1477 return QOpcUa::Types::ContentFilterElement;
1478 if (type == &UA_TYPES[UA_TYPES_EVENTFILTER])
1479 return QOpcUa::Types::EventFilter;
1480
1481 qCWarning(QT_OPCUA_PLUGINS_OPEN62541) << "Trying to convert unhandled type:" << (type ? type->typeName : "Unknown");
1482 return QOpcUa::Types::Undefined;
1483}
1484}
1485
1486QT_END_NAMESPACE
1487

source code of qtopcua/src/plugins/opcua/open62541/qopen62541valueconverter.cpp