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 | |
36 | QT_BEGIN_NAMESPACE |
37 | |
38 | Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_OPEN62541) |
39 | |
40 | using namespace QOpcUa::NodeIds; |
41 | |
42 | namespace QOpen62541ValueConverter { |
43 | |
44 | UA_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 | |
165 | QVariant 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 | |
257 | const 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 | |
348 | template<typename TARGETTYPE, typename UATYPE> |
349 | TARGETTYPE scalarToQt(const UATYPE *data) |
350 | { |
351 | return *reinterpret_cast<const TARGETTYPE *>(data); |
352 | } |
353 | |
354 | template<> |
355 | QString 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 | |
360 | template<> |
361 | QByteArray scalarToQt<QByteArray, UA_ByteString>(const UA_ByteString *data) |
362 | { |
363 | return QByteArray(reinterpret_cast<const char *>(data->data), data->length); |
364 | } |
365 | |
366 | template<> |
367 | QOpcUaLocalizedText 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 | |
375 | template<> |
376 | QString scalarToQt<QString, UA_NodeId>(const UA_NodeId *data) |
377 | { |
378 | return Open62541Utils::nodeIdToQString(id: *data); |
379 | } |
380 | |
381 | template<> |
382 | QDateTime 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 | |
392 | template<> |
393 | QOpcUaDataValue 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 | |
415 | template<> |
416 | QUuid 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 | |
422 | template<> |
423 | QOpcUaQualifiedName 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 | |
431 | template<> |
432 | QOpcUaArgument 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 | |
444 | template<> |
445 | QOpcUaRange scalarToQt<QOpcUaRange, UA_Range>(const UA_Range *data) |
446 | { |
447 | return QOpcUaRange(data->low, data->high); |
448 | } |
449 | |
450 | template<> |
451 | QOpcUaEUInformation 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 | |
459 | template<> |
460 | QOpcUaComplexNumber scalarToQt<QOpcUaComplexNumber, UA_ComplexNumberType>(const UA_ComplexNumberType *data) |
461 | { |
462 | return QOpcUaComplexNumber(data->real, data->imaginary); |
463 | } |
464 | |
465 | template<> |
466 | QOpcUaDoubleComplexNumber scalarToQt<QOpcUaDoubleComplexNumber, UA_DoubleComplexNumberType>( |
467 | const UA_DoubleComplexNumberType *data) |
468 | { |
469 | return QOpcUaDoubleComplexNumber(data->real, data->imaginary); |
470 | } |
471 | |
472 | template<> |
473 | QOpcUaAxisInformation 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 | |
489 | template<> |
490 | QOpcUaXValue scalarToQt<QOpcUaXValue, UA_XVType>(const UA_XVType *data) |
491 | { |
492 | return QOpcUaXValue(data->x, data->value); |
493 | } |
494 | |
495 | template<> |
496 | QOpcUaStructureField 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 | |
513 | template<> |
514 | QOpcUaStructureDefinition 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 | |
528 | template<> |
529 | QOpcUaEnumField 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 | |
540 | template<> |
541 | QOpcUaEnumDefinition 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 | |
553 | template<> |
554 | QOpcUaDiagnosticInfo 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 | |
597 | template<> |
598 | QOpcUaSimpleAttributeOperand 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 | |
615 | template<> |
616 | QOpcUaLiteralOperand 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 | |
625 | template<> |
626 | QOpcUaElementOperand scalarToQt<QOpcUaElementOperand, UA_ElementOperand>(const UA_ElementOperand *data) |
627 | { |
628 | QOpcUaElementOperand result; |
629 | result.setIndex(data->index); |
630 | |
631 | return result; |
632 | } |
633 | |
634 | template<> |
635 | QOpcUaRelativePathElement 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 | |
646 | template<> |
647 | QOpcUaAttributeOperand 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 | |
665 | template<> |
666 | QOpcUaContentFilterElement 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 | |
699 | template<> |
700 | QOpcUaMonitoringParameters::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 | |
715 | template <> |
716 | QVariant 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 | |
839 | template<> |
840 | QOpcUaExpandedNodeId 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 | |
849 | template<typename TARGETTYPE, typename UATYPE> |
850 | QVariant 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 | |
888 | template<typename TARGETTYPE, typename QTTYPE> |
889 | void scalarFromQt(const QTTYPE &value, TARGETTYPE *ptr) |
890 | { |
891 | *ptr = static_cast<TARGETTYPE>(value); |
892 | } |
893 | |
894 | template<> |
895 | void 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 | |
908 | template<> |
909 | void scalarFromQt<UA_String, QString>(const QString &value, UA_String *ptr) |
910 | { |
911 | *ptr = UA_STRING_ALLOC(value.toUtf8().constData()); |
912 | } |
913 | |
914 | template<> |
915 | void 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 | |
921 | template<> |
922 | void 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 | |
933 | template<> |
934 | void scalarFromQt<UA_NodeId, QString>(const QString &value, UA_NodeId *ptr) |
935 | { |
936 | *ptr = Open62541Utils::nodeIdFromQString(name: value); |
937 | } |
938 | |
939 | template<> |
940 | void 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 | |
946 | template<> |
947 | void 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 | |
955 | template<> |
956 | void scalarFromQt<UA_Range, QOpcUaRange>(const QOpcUaRange &value, UA_Range *ptr) |
957 | { |
958 | ptr->low = value.low(); |
959 | ptr->high = value.high(); |
960 | } |
961 | |
962 | template<> |
963 | void 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 | |
971 | template<> |
972 | void scalarFromQt<UA_ComplexNumberType, QOpcUaComplexNumber>(const QOpcUaComplexNumber &value, UA_ComplexNumberType *ptr) |
973 | { |
974 | ptr->real = value.real(); |
975 | ptr->imaginary = value.imaginary(); |
976 | } |
977 | |
978 | template<> |
979 | void scalarFromQt<UA_DoubleComplexNumberType, QOpcUaDoubleComplexNumber>(const QOpcUaDoubleComplexNumber &value, UA_DoubleComplexNumberType *ptr) |
980 | { |
981 | ptr->real = value.real(); |
982 | ptr->imaginary = value.imaginary(); |
983 | } |
984 | |
985 | template<> |
986 | void 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 | |
1004 | template<> |
1005 | void scalarFromQt<UA_XVType, QOpcUaXValue>(const QOpcUaXValue &value, UA_XVType *ptr) |
1006 | { |
1007 | ptr->x = value.x(); |
1008 | ptr->value = value.value(); |
1009 | } |
1010 | |
1011 | template<> |
1012 | void 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 | |
1030 | template<> |
1031 | void 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 | |
1046 | template<> |
1047 | void 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 | |
1055 | template<> |
1056 | void 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 | |
1067 | template<> |
1068 | void 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 | |
1107 | template<> |
1108 | void 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 | |
1116 | template<> |
1117 | void 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 | |
1124 | template<> |
1125 | void 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 | |
1138 | template<> |
1139 | void 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 | |
1153 | template<> |
1154 | void scalarFromQt<UA_LiteralOperand, QOpcUaLiteralOperand>(const QOpcUaLiteralOperand &value, UA_LiteralOperand *ptr) |
1155 | { |
1156 | ptr->value = toOpen62541Variant(value: value.value(), type: value.type()); |
1157 | } |
1158 | |
1159 | template<> |
1160 | void scalarFromQt<UA_ElementOperand, QOpcUaElementOperand>(const QOpcUaElementOperand &value, UA_ElementOperand *ptr) |
1161 | { |
1162 | ptr->index = value.index(); |
1163 | } |
1164 | |
1165 | template<> |
1166 | void 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 | |
1174 | template<> |
1175 | void 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 | |
1192 | template<> |
1193 | void 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 | |
1234 | template<> |
1235 | void 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 | |
1258 | template<typename TARGETTYPE, typename QTTYPE> |
1259 | UA_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 | |
1303 | void 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 | |
1320 | QVariant 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 | |
1341 | QOpcUaExtensionObject 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 | |
1371 | QOpcUa::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 | |
1461 | QT_END_NAMESPACE |
1462 | |