From b5f002f872052f126e7a6798862f3db5f5ea5ad2 Mon Sep 17 00:00:00 2001 From: Jannis Voelker Date: Thu, 9 Aug 2018 16:53:56 +0200 Subject: Allow omitting ns=0 in node ids OPC UA part 6, 5.3.1.10 states that the ns= clause in the XML encoded node id shall be omitted if the namespace index is 0. The string node ids in Qt OPC UA use the XML notation defined in part 6, so skipping the ns=0 part should be allowed. Change-Id: I7e688ebe191d43aac3ca43b209c9797fbd5c3086 Reviewed-by: Frank Meerkoetter --- src/opcua/client/qopcuatype.cpp | 53 +++++++++++++++++++++------------ src/opcua/client/qopcuatype.h | 1 + src/plugins/opcua/uacpp/quacpputils.cpp | 50 ++++++------------------------- 3 files changed, 44 insertions(+), 60 deletions(-) (limited to 'src') diff --git a/src/opcua/client/qopcuatype.cpp b/src/opcua/client/qopcuatype.cpp index dc838a4..30d1841 100644 --- a/src/opcua/client/qopcuatype.cpp +++ b/src/opcua/client/qopcuatype.cpp @@ -37,6 +37,7 @@ #include "qopcuatype.h" #include +#include #include QT_BEGIN_NAMESPACE @@ -812,41 +813,55 @@ QString QOpcUa::nodeIdFromReferenceType(QOpcUa::ReferenceTypeId referenceType) Returns \c true if the node id could be split successfully. For example, "ns=1;s=MyString" is split into 1, 's' and "MyString". + If no namespace index is given, ns=0 is assumed. */ bool QOpcUa::nodeIdStringSplit(const QString &nodeIdString, quint16 *nsIndex, QString *identifier, char *identifierType) { - QStringList components = nodeIdString.split(QLatin1String(";")); - QStringList result; + quint16 namespaceIndex = 0; - if (components.size() != 2) - return false; + QStringList components = nodeIdString.split(QLatin1String(";")); - if (components.at(0).contains(QRegExp(QLatin1String("/^ns=[0-9]+$/")))) + if (components.size() > 2) return false; - bool success = false; - uint ns = components.at(0).midRef(3).toString().toUInt(&success); - if (!success || ns > std::numeric_limits::max()) - return false; + if (components.size() == 2 && components.at(0).contains(QRegularExpression(QLatin1String("^ns=[0-9]+")))) { + bool success = false; + uint ns = components.at(0).midRef(3).toString().toUInt(&success); + if (!success || ns > std::numeric_limits::max()) + return false; + namespaceIndex = ns; + } - if (components.at(1).size() < 3) + if (components.last().size() < 3) return false; - if (!components.at(1).contains(QRegExp(QLatin1String("^[isgb]=")))) + if (!components.last().contains(QRegularExpression(QLatin1String("^[isgb]=")))) return false; if (nsIndex) - *nsIndex = ns; + *nsIndex = namespaceIndex; if (identifier) - *identifier = components.at(1).midRef(2).toString(); + *identifier = components.last().midRef(2).toString(); if (identifierType) - *identifierType = components.at(1).at(0).toLatin1(); - - result.append(components.at(1).midRef(2).toString()); + *identifierType = components.last().at(0).toLatin1(); return true; } +/*! + Returns \c true if the two node ids have the same namespace index and identifier. + A node id string without a namespace index is assumed to be in namespace 0. +*/ +bool QOpcUa::nodeIdEquals(const QString &first, const QString &second) +{ + if (first.startsWith(QLatin1String("ns=0;")) && !second.startsWith(QLatin1String("ns="))) + return first.midRef(5) == second; + else if (second.startsWith(QLatin1String("ns=0;")) && !first.startsWith(QLatin1String("ns="))) + return second.midRef(5) == first; + else + return first == second; +} + /*! Returns a node id string for the namespace 0 identifier \a id. */ @@ -1716,7 +1731,7 @@ QOpcUa::QExpandedNodeId &QOpcUa::QExpandedNodeId::operator=(const QOpcUa::QExpan bool QOpcUa::QExpandedNodeId::operator==(const QOpcUa::QExpandedNodeId &rhs) const { return data->namespaceUri == rhs.namespaceUri() && - data->nodeId == rhs.nodeId() && + nodeIdEquals(data->nodeId, rhs.nodeId()) && data->serverIndex == rhs.serverIndex(); } @@ -3495,7 +3510,7 @@ QOpcUa::QArgument &QOpcUa::QArgument::operator=(const QOpcUa::QArgument &rhs) bool QOpcUa::QArgument::operator==(const QOpcUa::QArgument &other) const { return data->arrayDimensions == other.arrayDimensions() && - data->dataTypeId == other.dataTypeId() && + nodeIdEquals(data->dataTypeId, other.dataTypeId()) && data->description == other.description() && data->name == other.name() && data->valueRank == other.valueRank(); @@ -3688,7 +3703,7 @@ QOpcUa::QExtensionObject &QOpcUa::QExtensionObject::operator=(const QOpcUa::QExt bool QOpcUa::QExtensionObject::operator==(const QOpcUa::QExtensionObject &rhs) const { return data->encoding == rhs.encoding() && - data->encodingTypeId == rhs.encodingTypeId() && + nodeIdEquals(data->encodingTypeId, rhs.encodingTypeId()) && data->encodedBody == rhs.encodedBody(); } diff --git a/src/opcua/client/qopcuatype.h b/src/opcua/client/qopcuatype.h index 3b372c2..bee0954 100644 --- a/src/opcua/client/qopcuatype.h +++ b/src/opcua/client/qopcuatype.h @@ -427,6 +427,7 @@ Q_OPCUA_EXPORT QString nodeIdFromInteger(quint16 ns, quint32 identifier); Q_OPCUA_EXPORT QString nodeIdFromReferenceType(QOpcUa::ReferenceTypeId referenceType); Q_OPCUA_EXPORT bool nodeIdStringSplit(const QString &nodeIdString, quint16 *nsIndex, QString *identifier, char *identifierType); +Q_OPCUA_EXPORT bool nodeIdEquals(const QString &first, const QString &second); Q_OPCUA_EXPORT QString ns0ID(QOpcUa::NodeIds::NS0 id); Q_OPCUA_EXPORT QOpcUa::NodeIds::NS0 ns0IDFromNodeId(const QString &nodeId); Q_OPCUA_EXPORT QString ns0IDName(QOpcUa::NodeIds::NS0 id); diff --git a/src/plugins/opcua/uacpp/quacpputils.cpp b/src/plugins/opcua/uacpp/quacpputils.cpp index 940016d..4d5853e 100644 --- a/src/plugins/opcua/uacpp/quacpputils.cpp +++ b/src/plugins/opcua/uacpp/quacpputils.cpp @@ -21,6 +21,8 @@ #include "quacpputils.h" +#include + #include #include #include @@ -36,50 +38,16 @@ namespace UACppUtils { UaNodeId nodeIdFromQString(const QString &name) { - const int semicolonIndex = name.indexOf(';'); + quint16 index = 0; + char identifierType = 0; + QString identifierString; - if (semicolonIndex <= 0) { + bool success = QOpcUa::nodeIdStringSplit(name, &index, &identifierString, &identifierType); + if (!success) { qCWarning(QT_OPCUA_PLUGINS_UACPP, "Unable to split node id string: %s", qUtf8Printable(name)); return UaNodeId(); } - QStringRef namespaceString = name.leftRef(semicolonIndex); - if (namespaceString.length() <= 3 || !namespaceString.startsWith(QLatin1String("ns="))) { - qCWarning(QT_OPCUA_PLUGINS_UACPP, "Not a valid index string in node id string: %s", qUtf8Printable(name)); - return UaNodeId(); - } - namespaceString = namespaceString.mid(3); // Remove "ns=" - - QStringRef identifierString = name.midRef(semicolonIndex + 1); - - if (identifierString.length() <= 2) { - qCWarning(QT_OPCUA_PLUGINS_UACPP, "There is no identifier in node id string: %s", qUtf8Printable(name)); - return UaNodeId(); - } - - char identifierType; - if (identifierString.startsWith(QLatin1String("s="))) - identifierType = 's'; - else if (identifierString.startsWith(QLatin1String("i="))) - identifierType = 'i'; - else if (identifierString.startsWith(QLatin1String("g="))) - identifierType = 'g'; - else if (identifierString.startsWith(QLatin1String("b="))) - identifierType = 'b'; - else { - qCWarning(QT_OPCUA_PLUGINS_UACPP, "There is no valid identifier type in node id string: %s", qUtf8Printable(name)); - return UaNodeId(); - } - identifierString = identifierString.mid(2); // Remove identifier type - - bool ok = false; - OpcUa_UInt16 index = static_cast(namespaceString.toUInt(&ok)); - - if (!ok) { - qCWarning(QT_OPCUA_PLUGINS_UACPP, "Not a valid namespace index in node id string: %s", qUtf8Printable(name)); - return UaNodeId(); - } - switch (identifierType) { case 'i': { bool isNumber; @@ -98,7 +66,7 @@ UaNodeId nodeIdFromQString(const QString &name) break; } case 'g': { - QUuid uuid(identifierString.toString()); + QUuid uuid(identifierString); if (uuid.isNull()) { qCWarning(QT_OPCUA_PLUGINS_UACPP, "%s does not contain a valid guid identifier", qUtf8Printable(name)); @@ -112,7 +80,7 @@ UaNodeId nodeIdFromQString(const QString &name) return UaNodeId(guid, index); } case 'b': { - QByteArray temp = QByteArray::fromBase64(identifierString.toLocal8Bit()); + QByteArray temp = QByteArray::fromBase64(identifierString.toLatin1()); UaByteString bstr((OpcUa_Int32)temp.size(), reinterpret_cast(temp.data())); if (temp.size() > 0) { return UaNodeId(bstr, index); -- cgit v1.2.3