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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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