1// Copyright (C) 2015 basysKom GmbH, opensource@basyskom.com
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qopcuamultidimensionalarray.h"
5
6QT_BEGIN_NAMESPACE
7
8/*
9 This class has been modelled in the style of the Variant encoding
10 defined in OPC UA 1.05 part 6, 5.2.2.16.
11
12 This solution has been preferred to returning nested QVariantLists
13 due to the following reasons:
14 - A QVariantList inside a QVariantList is stored as a QVariant which must be converted
15 to QVariantList before the elements can be accessed. This makes it impossible to update the
16 values in place.
17 - The length of the array is encoded as a 32 bit unsigned integer.
18 Array dimensions are encoded in an array, so an array can have UINT32_MAX dimensions.
19 Depending on the number of dimensions, there could be lots of nested QVariantLists
20 which would require a huge effort when calculating the array dimensions for conversions
21 between QVariantList and the sdk specific variant type.
22*/
23
24/*!
25 \class QOpcUaMultiDimensionalArray
26 \inmodule QtOpcUa
27 \brief A container class for multidimensional arrays.
28
29 This class manages arrays of Qt OPC UA types with associated array dimensions information.
30 It is returned as value when a multidimensional array is received from the server. It can also
31 be used as a write value or as parameter for filters and method calls.
32*/
33
34class QOpcUaMultiDimensionalArrayData : public QSharedData
35{
36public:
37 QVariantList value;
38 QList<quint32> arrayDimensions;
39 quint32 expectedArrayLength{0};
40};
41
42/*!
43 Default constructs a multi dimensional array with no parameters set.
44*/
45QOpcUaMultiDimensionalArray::QOpcUaMultiDimensionalArray()
46 : data(new QOpcUaMultiDimensionalArrayData)
47{
48}
49
50/*!
51 Constructs a multidimensional array from \a other.
52*/
53QOpcUaMultiDimensionalArray::QOpcUaMultiDimensionalArray(const QOpcUaMultiDimensionalArray &other)
54 : data(other.data)
55{
56}
57
58/*!
59 Sets the values from \a rhs in the multidimensional array.
60*/
61QOpcUaMultiDimensionalArray &QOpcUaMultiDimensionalArray::operator=(const QOpcUaMultiDimensionalArray &rhs)
62{
63 if (this != &rhs)
64 data.operator=(o: rhs.data);
65 return *this;
66}
67
68/*!
69 Constructs a multidimensional array with value \a value and array dimensions \a arrayDimensions.
70*/
71QOpcUaMultiDimensionalArray::QOpcUaMultiDimensionalArray(const QVariantList &value, const QList<quint32> &arrayDimensions)
72 : data(new QOpcUaMultiDimensionalArrayData)
73{
74 setValueArray(value);
75 setArrayDimensions(arrayDimensions);
76}
77
78/*!
79 Creates a multidimensional array with preallocated data fitting \a arrayDimensions.
80*/
81QOpcUaMultiDimensionalArray::QOpcUaMultiDimensionalArray(const QList<quint32> &arrayDimensions)
82 : data(new QOpcUaMultiDimensionalArrayData)
83{
84 setArrayDimensions(arrayDimensions);
85 if (data->expectedArrayLength) {
86 data->value.reserve(asize: data->expectedArrayLength);
87 for (size_t i = 0; i < data->expectedArrayLength; ++i)
88 data->value.append(t: QVariant());
89 }
90}
91
92QOpcUaMultiDimensionalArray::~QOpcUaMultiDimensionalArray()
93{
94}
95
96/*!
97 Returns the dimensions of the multidimensional array.
98 The element at position n contains the length of the n-th dimension.
99*/
100QList<quint32> QOpcUaMultiDimensionalArray::arrayDimensions() const
101{
102 return data->arrayDimensions;
103}
104
105/*!
106 Sets the dimensions of the multidimensional array to \a arrayDimensions.
107*/
108void QOpcUaMultiDimensionalArray::setArrayDimensions(const QList<quint32> &arrayDimensions)
109{
110 data->arrayDimensions = arrayDimensions;
111 data->expectedArrayLength = std::accumulate(first: data->arrayDimensions.begin(), last: data->arrayDimensions.end(),
112 init: 1, binary_op: std::multiplies<quint32>());
113}
114
115/*!
116 Returns \c true if this multidimensional array has the same value as \a other.
117*/
118bool QOpcUaMultiDimensionalArray::operator==(const QOpcUaMultiDimensionalArray &other) const
119{
120 return arrayDimensions() == other.arrayDimensions() &&
121 valueArray() == other.valueArray();
122}
123
124/*!
125 Converts this multidimensional array to \l QVariant.
126*/
127QOpcUaMultiDimensionalArray::operator QVariant() const
128{
129 return QVariant::fromValue(value: *this);
130}
131
132/*!
133 Returns the value array of the multidimensional array.
134*/
135QVariantList QOpcUaMultiDimensionalArray::valueArray() const
136{
137 return data->value;
138}
139
140/*!
141 Returns a reference to the value array of the multidimensional array.
142*/
143QVariantList &QOpcUaMultiDimensionalArray::valueArrayRef()
144{
145 return data->value;
146}
147
148/*!
149 Sets the value array of the multidimensional array to \a value.
150*/
151void QOpcUaMultiDimensionalArray::setValueArray(const QVariantList &value)
152{
153 data->value = value;
154}
155
156/*!
157 Returns the array index in \l valueArray() of the element identified by \a indices.
158 If \a indices is invalid for the array or if the array's dimensions don't match
159 the size of \l valueArray(), the invalid index \c -1 is returned.
160*/
161int QOpcUaMultiDimensionalArray::arrayIndex(const QList<quint32> &indices) const
162{
163 // A QList can store INT_MAX values. Depending on the platform, this allows a size > UINT32_MAX
164 if (data->expectedArrayLength > static_cast<quint64>((std::numeric_limits<int>::max)()) ||
165 static_cast<quint64>(data->value.size()) > (std::numeric_limits<quint32>::max)())
166 return -1;
167
168 // Check number of dimensions and data size
169 if (indices.size() != data->arrayDimensions.size() ||
170 data->expectedArrayLength != static_cast<quint32>(data->value.size()))
171 return -1; // Missing array dimensions or array dimensions don't fit the array
172
173 quint32 index = 0;
174 quint32 stride = 1;
175 // Reverse iteration to avoid repetitions while calculating the stride
176 for (int i = data->arrayDimensions.size() - 1; i >= 0; --i) {
177 if (indices.at(i) >= data->arrayDimensions.at(i)) // Out of bounds
178 return -1;
179
180 // Arrays are encoded in row-major order: [0,0,0], [0,0,1], [0,1,0], [0,1,1], [1,0,0], [1,0,1], [1,1,0], [1,1,1]
181 // The stride for dimension i in a n dimensional array is the product of all array dimensions from i+1 to n
182 if (i < data->arrayDimensions.size() - 1)
183 stride *= data->arrayDimensions.at(i: i + 1);
184 index += stride * indices.at(i);
185 }
186
187 return (index <= static_cast<quint64>((std::numeric_limits<int>::max)())) ?
188 static_cast<int>(index) : -1;
189}
190
191/*!
192 Returns the value of the element identified by \a indices.
193 If the indices are invalid for the array, an empty \l QVariant is returned.
194*/
195QVariant QOpcUaMultiDimensionalArray::value(const QList<quint32> &indices) const
196{
197 int index = arrayIndex(indices);
198
199 if (index < 0)
200 return QVariant();
201
202 return data->value.at(i: index);
203}
204
205/*!
206 Sets the value at position \a indices to \a value.
207 Returns \c true if the value has been successfully set.
208*/
209bool QOpcUaMultiDimensionalArray::setValue(const QList<quint32> &indices, const QVariant &value)
210{
211 int index = arrayIndex(indices);
212
213 if (index < 0)
214 return false;
215
216 data->value[index] = value;
217 return true;
218}
219
220/*!
221 Returns \c true if the multidimensional array is valid
222*/
223bool QOpcUaMultiDimensionalArray::isValid() const
224{
225 return static_cast<quint64>(data->value.size()) == data->expectedArrayLength &&
226 static_cast<quint64>(data->value.size()) <= (std::numeric_limits<quint32>::max)() &&
227 static_cast<quint64>(data->arrayDimensions.size()) <= (std::numeric_limits<quint32>::max)();
228}
229
230QT_END_NAMESPACE
231

Provided by KDAB

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

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