summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorJannis Voelker <jannis.voelker@basyskom.com>2018-04-30 14:24:26 +0200
committerJannis Völker <jannis.voelker@basyskom.com>2018-07-27 12:13:41 +0000
commitc20a8242974bc2c580ab84c998e6346b03ccd33c (patch)
tree55ec5143cbdfcf1d2adf08b57d08755e25b7a2ca /src
parent735fd6aed2fe3d853d863113c42f60f2227e377e (diff)
Enable reading and writing of multi-dimensional arrays
This patch proposes a way to handle multi-dimensional arrays in Qt OPC UA. For now, support has only been implemented in the open62541 backend. A test case showing how to handle multi-dimensional arrays using the approach implemented in this patch has been added to tst_client.cpp. Change-Id: Iabe58fee405989898469c13a1c0ed51b5d057098 Reviewed-by: Rainer Keller <Rainer.Keller@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/opcua/client/qopcuatype.cpp220
-rw-r--r--src/opcua/client/qopcuatype.h34
-rw-r--r--src/opcua/doc/src/qtopcua.qdoc5
-rw-r--r--src/plugins/opcua/open62541/qopen62541valueconverter.cpp24
4 files changed, 282 insertions, 1 deletions
diff --git a/src/opcua/client/qopcuatype.cpp b/src/opcua/client/qopcuatype.cpp
index 21424cc..367a24b 100644
--- a/src/opcua/client/qopcuatype.cpp
+++ b/src/opcua/client/qopcuatype.cpp
@@ -3699,4 +3699,224 @@ void QOpcUa::QExtensionObject::setEncodingTypeId(const QString &value)
data->encodingTypeId = value;
}
+/*
+ This class has been modelled in the style of the Variant encoding
+ defined in OPC-UA part 6, 5.2.2.16.
+
+ This solution has been preferred to returning nested QVariantLists
+ due to the following reasons:
+ - A QVariantList inside a QVariantList is stored as a QVariant which must be converted
+ to QVariantList before the elements can be accessed. This makes it impossible to update the
+ values in place.
+ - The length of the array is encoded as a 32 bit unsigned integer.
+ Array dimensions are encoded in an array, so an array can have UINT32_MAX dimensions.
+ Depending on the number of dimensions, there could be lots of nested QVariantLists
+ which would require a huge effort when calculating the array dimensions for conversions
+ between QVariantList and the sdk specific variant type.
+*/
+
+/*!
+ \class QOpcUa::QMultiDimensionalArray
+ \inmodule QtOpcUa
+ \inheaderfile QtOpcUa/qopcuatype.h
+ \brief A container class for multidimensional arrays.
+
+ This class manages arrays of Qt OPC UA types with associated array dimensions information.
+ It is returned as value when a multidimensional array is received from the server. It can also
+ be used as a write value or as parameter for filters and method calls.
+*/
+
+class QOpcUa::QMultiDimensionalArrayData : public QSharedData
+{
+public:
+ QVariantList value;
+ QVector<quint32> arrayDimensions;
+ quint32 expectedArrayLength{0};
+};
+
+QOpcUa::QMultiDimensionalArray::QMultiDimensionalArray()
+ : data(new QOpcUa::QMultiDimensionalArrayData)
+{
+}
+
+/*!
+ Constructs a multidimensional array from \a other.
+*/
+QOpcUa::QMultiDimensionalArray::QMultiDimensionalArray(const QOpcUa::QMultiDimensionalArray &other)
+ : data(other.data)
+{
+}
+
+/*!
+ Sets the values from \a rhs in the multidimensional array.
+*/
+QOpcUa::QMultiDimensionalArray &QOpcUa::QMultiDimensionalArray::operator=(const QOpcUa::QMultiDimensionalArray &rhs)
+{
+ if (this != &rhs)
+ data.operator=(rhs.data);
+ return *this;
+}
+
+/*!
+ Constructs a multidimensional array with value \a value and array dimensions \a arrayDimensions.
+*/
+QOpcUa::QMultiDimensionalArray::QMultiDimensionalArray(const QVariantList &value, const QVector<quint32> &arrayDimensions)
+ : data(new QOpcUa::QMultiDimensionalArrayData)
+{
+ setValueArray(value);
+ setArrayDimensions(arrayDimensions);
+}
+
+/*!
+ Creates a multidimensional array with preallocated data fitting \a arrayDimensions.
+*/
+QOpcUa::QMultiDimensionalArray::QMultiDimensionalArray(const QVector<quint32> &arrayDimensions)
+ : data(new QOpcUa::QMultiDimensionalArrayData)
+{
+ setArrayDimensions(arrayDimensions);
+ if (data->expectedArrayLength) {
+ data->value.reserve(data->expectedArrayLength);
+ for (size_t i = 0; i < data->expectedArrayLength; ++i)
+ data->value.append(QVariant());
+ }
+}
+
+QOpcUa::QMultiDimensionalArray::~QMultiDimensionalArray()
+{
+}
+
+/*!
+ Returns the dimensions of the multidimensional array.
+ The element at position n contains the length of the n-th dimension.
+*/
+QVector<quint32> QOpcUa::QMultiDimensionalArray::arrayDimensions() const
+{
+ return data->arrayDimensions;
+}
+
+/*!
+ Sets the dimensions of the multidimensional array to \a arrayDimensions.
+*/
+void QOpcUa::QMultiDimensionalArray::setArrayDimensions(const QVector<quint32> &arrayDimensions)
+{
+ data->arrayDimensions = arrayDimensions;
+ data->expectedArrayLength = std::accumulate(data->arrayDimensions.begin(), data->arrayDimensions.end(),
+ 1, std::multiplies<quint32>());
+}
+
+/*!
+ Returns \c true if this multidimensional array has the same value as \a other.
+*/
+bool QOpcUa::QMultiDimensionalArray::operator==(const QOpcUa::QMultiDimensionalArray &other) const
+{
+ return arrayDimensions() == other.arrayDimensions() &&
+ valueArray() == other.valueArray();
+}
+
+/*!
+ Converts this multidimensional array to \l QVariant.
+*/
+QOpcUa::QMultiDimensionalArray::operator QVariant() const
+{
+ return QVariant::fromValue(*this);
+}
+
+/*!
+ Returns the value array of the multidimensional array.
+*/
+QVariantList QOpcUa::QMultiDimensionalArray::valueArray() const
+{
+ return data->value;
+}
+
+/*!
+ Returns a reference to the value array of the multidimensional array.
+*/
+QVariantList &QOpcUa::QMultiDimensionalArray::valueArrayRef()
+{
+ return data->value;
+}
+
+/*!
+ Sets the value array of the multidimensional array to \a value.
+*/
+void QOpcUa::QMultiDimensionalArray::setValueArray(const QVariantList &value)
+{
+ data->value = value;
+}
+
+/*!
+ Returns the array index in \l valueArray() of the element identified by \a indices.
+ If \a indices is invalid for the array or if the array's dimensions don't match
+ the size of \l valueArray(), the invalid index \c -1 is returned.
+*/
+int QOpcUa::QMultiDimensionalArray::arrayIndex(const QVector<quint32> &indices) const
+{
+ // A QList can store INT_MAX values. Depending on the platform, this allows a size > UINT32_MAX
+ if (data->expectedArrayLength > static_cast<quint64>(std::numeric_limits<int>::max()) ||
+ static_cast<quint64>(data->value.size()) > std::numeric_limits<quint32>::max())
+ return -1;
+
+ // Check number of dimensions and data size
+ if (indices.size() != data->arrayDimensions.size() ||
+ data->expectedArrayLength != static_cast<quint32>(data->value.size()))
+ return -1; // Missing array dimensions or array dimensions don't fit the array
+
+ quint32 index = 0;
+ quint32 stride = 1;
+ // Reverse iteration to avoid repetitions while calculating the stride
+ for (int i = data->arrayDimensions.size() - 1; i >= 0; --i) {
+ if (indices.at(i) >= data->arrayDimensions.at(i)) // Out of bounds
+ return -1;
+
+ // 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]
+ // The stride for dimension i in a n dimensional array is the product of all array dimensions from i+1 to n
+ if (i < data->arrayDimensions.size() - 1)
+ stride *= data->arrayDimensions.at(i + 1);
+ index += stride * indices.at(i);
+ }
+
+ return (index <= static_cast<quint64>(std::numeric_limits<int>::max())) ?
+ static_cast<int>(index) : -1;
+}
+
+/*!
+ Returns the value of the element identified by \a indices.
+ If the indices are invalid for the array, an empty \l QVariant is returned.
+*/
+QVariant QOpcUa::QMultiDimensionalArray::value(const QVector<quint32> &indices) const
+{
+ int index = arrayIndex(indices);
+
+ if (index < 0)
+ return QVariant();
+
+ return data->value.at(index);
+}
+
+/*!
+ Sets the value at position \a indices to \a value.
+ Returns \c true if the value has been successfully set.
+*/
+bool QOpcUa::QMultiDimensionalArray::setValue(const QVector<quint32> &indices, const QVariant &value)
+{
+ int index = arrayIndex(indices);
+
+ if (index < 0)
+ return false;
+
+ data->value[index] = value;
+ return true;
+}
+
+/*!
+ Returns \c true if the multidimensional array is valid
+*/
+bool QOpcUa::QMultiDimensionalArray::isValid() const
+{
+ return static_cast<quint64>(data->value.size()) == data->expectedArrayLength &&
+ static_cast<quint64>(data->value.size()) <= std::numeric_limits<quint32>::max() &&
+ static_cast<quint64>(data->arrayDimensions.size()) <= std::numeric_limits<quint32>::max();
+}
+
QT_END_NAMESPACE
diff --git a/src/opcua/client/qopcuatype.h b/src/opcua/client/qopcuatype.h
index 24d11fc..742bdc0 100644
--- a/src/opcua/client/qopcuatype.h
+++ b/src/opcua/client/qopcuatype.h
@@ -1092,6 +1092,39 @@ public:
private:
QSharedDataPointer<QOpcUa::QExtensionObjectData> data;
};
+
+class QMultiDimensionalArrayData;
+class Q_OPCUA_EXPORT QMultiDimensionalArray
+{
+public:
+ QMultiDimensionalArray();
+ QMultiDimensionalArray(const QOpcUa::QMultiDimensionalArray &other);
+ QMultiDimensionalArray &operator=(const QOpcUa::QMultiDimensionalArray &rhs);
+ QMultiDimensionalArray(const QVariantList &valueArray, const QVector<quint32> &arrayDimensions);
+ QMultiDimensionalArray(const QVector<quint32> &arrayDimensions);
+ ~QMultiDimensionalArray();
+
+ QVariantList valueArray() const;
+ QVariantList &valueArrayRef();
+ void setValueArray(const QVariantList &valueArray);
+
+ int arrayIndex(const QVector<quint32> &indices) const;
+ QVariant value(const QVector<quint32> &indices) const;
+ bool setValue(const QVector<quint32> &indices, const QVariant &value);
+
+ bool isValid() const;
+
+ QVector<quint32> arrayDimensions() const;
+ void setArrayDimensions(const QVector<quint32> &arrayDimensions);
+
+ bool operator==(const QOpcUa::QMultiDimensionalArray &other) const;
+
+ operator QVariant() const;
+
+private:
+ QSharedDataPointer<QOpcUa::QMultiDimensionalArrayData> data;
+};
+
}
Q_DECLARE_TYPEINFO(QOpcUa::Types, Q_PRIMITIVE_TYPE);
@@ -1138,5 +1171,6 @@ Q_DECLARE_METATYPE(QOpcUa::QApplicationDescription)
Q_DECLARE_METATYPE(QOpcUa::QEndpointDescription)
Q_DECLARE_METATYPE(QOpcUa::QArgument)
Q_DECLARE_METATYPE(QOpcUa::QExtensionObject)
+Q_DECLARE_METATYPE(QOpcUa::QMultiDimensionalArray)
#endif // QOPCUATYPE
diff --git a/src/opcua/doc/src/qtopcua.qdoc b/src/opcua/doc/src/qtopcua.qdoc
index be8e693..a0c9f08 100644
--- a/src/opcua/doc/src/qtopcua.qdoc
+++ b/src/opcua/doc/src/qtopcua.qdoc
@@ -109,6 +109,10 @@
\li X
\li X
\row
+ \li Multidimensional arrays
+ \li X
+ \li
+ \row
\li Browse
\li X
\li X
@@ -351,5 +355,4 @@
\code
Unified Automation C++ SDK ............. yes
\endcode
-
*/
diff --git a/src/plugins/opcua/open62541/qopen62541valueconverter.cpp b/src/plugins/opcua/open62541/qopen62541valueconverter.cpp
index 5662695..2ff527f 100644
--- a/src/plugins/opcua/open62541/qopen62541valueconverter.cpp
+++ b/src/plugins/opcua/open62541/qopen62541valueconverter.cpp
@@ -95,6 +95,20 @@ UA_Variant toOpen62541Variant(const QVariant &value, QOpcUa::Types type)
UA_Variant open62541value;
UA_Variant_init(&open62541value);
+ if (value.canConvert<QOpcUa::QMultiDimensionalArray>()) {
+ QOpcUa::QMultiDimensionalArray data = value.value<QOpcUa::QMultiDimensionalArray>();
+ UA_Variant result = toOpen62541Variant(data.valueArray(), type);
+
+ if (!data.arrayDimensions().isEmpty()) {
+ // Ensure that the array dimensions size is < UINT32_MAX
+ if (static_cast<quint64>(data.arrayDimensions().size()) > std::numeric_limits<quint32>::max())
+ return open62541value;
+ result.arrayDimensionsSize = data.arrayDimensions().size();
+ result.arrayDimensions = static_cast<UA_UInt32 *>(UA_Array_new(result.arrayDimensionsSize, &UA_TYPES[UA_TYPES_UINT32]));
+ std::copy(data.arrayDimensions().constBegin(), data.arrayDimensions().constEnd(), result.arrayDimensions);
+ }
+ return result;
+ }
if (value.type() == QVariant::List && value.toList().size() == 0)
return open62541value;
@@ -455,6 +469,16 @@ QVariant arrayToQVariant(const UA_Variant &var, QMetaType::Type type)
tempVar.convert(type);
list.append(tempVar);
}
+
+ if (var.arrayDimensionsSize > 0) {
+ // Ensure that the array dimensions fit in a QVector
+ if (var.arrayDimensionsSize > static_cast<quint64>(std::numeric_limits<int>::max()))
+ return QOpcUa::QMultiDimensionalArray();
+ QVector<quint32> arrayDimensions;
+ std::copy(var.arrayDimensions, var.arrayDimensions+var.arrayDimensionsSize, std::back_inserter(arrayDimensions));
+ return QOpcUa::QMultiDimensionalArray(list, arrayDimensions);
+ }
+
if (list.size() == 1)
return list.at(0);
else