diff options
Diffstat (limited to 'src/imports/opcua/opcuanode.cpp')
-rw-r--r-- | src/imports/opcua/opcuanode.cpp | 277 |
1 files changed, 264 insertions, 13 deletions
diff --git a/src/imports/opcua/opcuanode.cpp b/src/imports/opcua/opcuanode.cpp index 28fe73c..d81f287 100644 --- a/src/imports/opcua/opcuanode.cpp +++ b/src/imports/opcua/opcuanode.cpp @@ -56,7 +56,7 @@ QT_BEGIN_NAMESPACE \since QtOpcUa 5.12 \code - import QtOpcUa 5.12 as QtOpcUa + import QtOpcUa 5.13 as QtOpcUa QtOpcUa.Node { nodeId : QtOpcUa.NodeId { @@ -134,12 +134,59 @@ QT_BEGIN_NAMESPACE a default constructed \l LocalizedText is returned. */ +/*! + \qmlproperty Status Node::status + \readonly + + Current status of the node. The node is only usable when the status is \c Valid. + In any other case it indicates an error. + + \sa errorMessage, Status +*/ + +/*! + \qmlproperty string Node::errorMessage + \readonly + + A string representation of the current status code. + + \sa status, Status +*/ + +/*! + \qmlproperty enumeration Node::Status + + Status of a node. + + \value Node.Status.Valid Node is ready for use + \value Node.Status.InvalidNodeId Node id is invalid + \value Node.Status.NoConnection Not connected to a server + \value Node.Status.InvalidNodeType Node type on the server does not match the QML type + \value Node.Status.InvalidClient Invalid connection client + \value Node.Status.FailedToResolveNode Failed to resolve node + \value Node.Status.InvalidObjectNode The object node is invalid or its type does not match the expected type + \value Node.Status.FailedToReadAttributes Failed to read attributes + \value Node.Status.FailedToSetupMonitoring Failed to setup monitoring + + \sa status, errorMessage +*/ + +/*! + \qmlproperty EventFilter Node::eventFilter + \since 5.13 + + An event filter allows monitoring events on the server for certain conditions. + + \sa EventFilter +*/ + Q_DECLARE_LOGGING_CATEGORY(QT_OPCUA_PLUGINS_QML) OpcUaNode::OpcUaNode(QObject *parent): QObject(parent), m_nodeId(new OpcUaNodeIdType(this)), - m_attributesToRead(QOpcUaNode::mandatoryBaseAttributes()) + m_attributesToRead(QOpcUaNode::mandatoryBaseAttributes()), + m_status(Status::InvalidNodeId) { m_attributesToRead |= QOpcUa::NodeAttribute::Description; connect(&m_resolvedNode, &UniversalNode::nodeChanged, this, &OpcUaNode::nodeChanged); @@ -179,12 +226,12 @@ void OpcUaNode::setBrowseName(const QString &value) if (!m_resolvedNode.isNamespaceIndexValid()) return; - m_node->writeAttribute(QOpcUa::NodeAttribute::BrowseName, QOpcUa::QQualifiedName(m_resolvedNode.namespaceIndex(), value)); + m_node->writeAttribute(QOpcUa::NodeAttribute::BrowseName, QOpcUaQualifiedName(m_resolvedNode.namespaceIndex(), value)); } QString OpcUaNode::browseName() { - return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::BrowseName).value<QOpcUa::QQualifiedName>().name(); + return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::BrowseName).value<QOpcUaQualifiedName>().name(); } QOpcUa::NodeClass OpcUaNode::nodeClass() @@ -192,28 +239,62 @@ QOpcUa::NodeClass OpcUaNode::nodeClass() return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::NodeClass).value<QOpcUa::NodeClass>(); } -void OpcUaNode::setDisplayName(const QOpcUa::QLocalizedText &value) +void OpcUaNode::setDisplayName(const QOpcUaLocalizedText &value) { if (!m_connection || !m_node) return; m_node->writeAttribute(QOpcUa::NodeAttribute::DisplayName, value); } -QOpcUa::QLocalizedText OpcUaNode::displayName() +QOpcUaLocalizedText OpcUaNode::displayName() { - return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::DisplayName).value<QOpcUa::QLocalizedText>(); + return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::DisplayName).value<QOpcUaLocalizedText>(); } -void OpcUaNode::setDescription(const QOpcUa::QLocalizedText &value) +void OpcUaNode::setDescription(const QOpcUaLocalizedText &value) { if (!m_connection || !m_node) return; m_node->writeAttribute(QOpcUa::NodeAttribute::Description, value); } -QOpcUa::QLocalizedText OpcUaNode::description() +QOpcUaLocalizedText OpcUaNode::description() +{ + return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::Description).value<QOpcUaLocalizedText>(); +} + +OpcUaNode::Status OpcUaNode::status() const +{ + return m_status; +} + +const QString &OpcUaNode::errorMessage() const +{ + return m_errorMessage; +} + +/*! + \qmlmethod Date Node::getSourceTimestamp(QOpcUa::NodeAttribute attribute) + + Returns the source timestamp of the given \a attribute. +*/ +QDateTime OpcUaNode::getSourceTimestamp(QOpcUa::NodeAttribute attribute) const +{ + if (!m_connection || !m_node) + return QDateTime(); + return m_node->sourceTimestamp(attribute); +} + +/*! + \qmlmethod Date Node::getServerTimestamp(Constants::NodeAttribute attribute) + + Returns the server timestamp of the given \a attribute. +*/ +QDateTime OpcUaNode::getServerTimestamp(QOpcUa::NodeAttribute attribute) const { - return m_attributeCache.attributeValue(QOpcUa::NodeAttribute::Description).value<QOpcUa::QLocalizedText>(); + if (!m_connection || !m_node) + return QDateTime(); + return m_node->serverTimestamp(attribute); } void OpcUaNode::setNodeId(OpcUaNodeIdType *nodeId) @@ -278,9 +359,52 @@ void OpcUaNode::setupNode(const QString &absoluteNodePath) setReadyToUse(true); }); + connect(m_node, &QOpcUaNode::enableMonitoringFinished, this, [this](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode){ + if (attr != QOpcUa::NodeAttribute::EventNotifier) + return; + if (statusCode == QOpcUa::Good) { + m_eventFilterActive = true; + qCDebug(QT_OPCUA_PLUGINS_QML) << "Event filter was enabled for node" << resolvedNode().fullNodeId(); + updateEventFilter(); + } else { + qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to enable event filter for node" << resolvedNode().fullNodeId(); + setStatus(Status::FailedToSetupMonitoring); + } + }); + + connect(m_node, &QOpcUaNode::disableMonitoringFinished, this, [this](QOpcUa::NodeAttribute attr, QOpcUa::UaStatusCode statusCode){ + if (attr != QOpcUa::NodeAttribute::EventNotifier) + return; + if (statusCode == QOpcUa::Good) { + m_eventFilterActive = false; + qCDebug(QT_OPCUA_PLUGINS_QML) << "Event filter was disabled for node "<< resolvedNode().fullNodeId(); + } else { + qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to disable event filter for node "<< resolvedNode().fullNodeId(); + setStatus(Status::FailedToDisableMonitoring); + } + }); + + connect(m_node, &QOpcUaNode::monitoringStatusChanged, this, [this](QOpcUa::NodeAttribute attr, QOpcUaMonitoringParameters::Parameters items, + QOpcUa::UaStatusCode statusCode) { + Q_UNUSED(items); + if (attr != QOpcUa::NodeAttribute::EventNotifier) + return; + if (statusCode != QOpcUa::Good) { + setStatus(Status::FailedToModifyMonitoring); + qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to modify event filter for" << m_node->nodeId(); + } + }); + + connect (m_node, &QOpcUaNode::eventOccurred, this, &OpcUaNode::eventOccurred); + + // Read mandatory attributes - if (!m_node->readAttributes(m_attributesToRead)) + if (!m_node->readAttributes(m_attributesToRead)) { qCWarning(QT_OPCUA_PLUGINS_QML) << "Reading attributes" << m_node->nodeId() << "failed"; + setStatus(Status::FailedToReadAttributes); + } + + updateEventFilter(); } void OpcUaNode::updateNode() @@ -288,6 +412,109 @@ void OpcUaNode::updateNode() retrieveAbsoluteNodePath(m_nodeId, [this](const QString &absoluteNodePath) {setupNode(absoluteNodePath);}); } +OpcUaEventFilter *OpcUaNode::eventFilter() const +{ + return m_eventFilter; +} + +void OpcUaNode::setEventFilter(OpcUaEventFilter *eventFilter) +{ + bool changed = false; + + if (m_eventFilter) { + disconnect(m_eventFilter, &OpcUaEventFilter::dataChanged, this, &OpcUaNode::updateEventFilter); + changed = !(*m_eventFilter == *eventFilter); + } else { + changed = true; + } + + m_eventFilter = eventFilter; + connect(m_eventFilter, &OpcUaEventFilter::dataChanged, this, &OpcUaNode::updateEventFilter); + + if (changed) + emit eventFilterChanged(); +} + + +void OpcUaNode::updateEventFilter() +{ + if (!m_connection || !m_node || !m_eventFilter) + return; + + if (m_eventFilterActive) { + m_node->modifyEventFilter(m_eventFilter->filter(m_connection->m_client)); + } else { + QOpcUaMonitoringParameters parameters; + parameters.setFilter(m_eventFilter->filter(m_connection->m_client)); + m_node->enableMonitoring(QOpcUa::NodeAttribute::EventNotifier, parameters); + m_eventFilterActive = true; + } +} + +void OpcUaNode::setStatus(OpcUaNode::Status status, const QString &message) +{ + QString errorMessage(message); + bool emitStatusChanged = false; + bool emitErrorMessageChanged = false; + + if (m_status != status) { + m_status = status; + emitStatusChanged = true; + } + + // if error message is not given, use default error message + if (errorMessage.isEmpty()) { + switch (m_status) { + case Status::Valid: + errorMessage = tr("Node is valid"); + break; + case Status::InvalidNodeId: + errorMessage = tr("Node Id is invalid"); + break; + case Status::NoConnection: + errorMessage = tr("Not connected to server"); + break; + case Status::InvalidNodeType: + errorMessage = tr("QML element does not match node type on the server"); + break; + case Status::InvalidClient: + errorMessage = tr("Connecting client is invalid"); + break; + case Status::FailedToResolveNode: + errorMessage = tr("Failed to resolve node"); + break; + case Status::InvalidObjectNode: + errorMessage = tr("Invalid object node"); + break; + case Status::FailedToReadAttributes: + errorMessage = tr("Failed to read attributes"); + break; + case Status::FailedToSetupMonitoring: + errorMessage = tr("Failed to setup monitoring"); + break; + case Status::FailedToWriteAttribute: + errorMessage = tr("Failed to write attribute"); + break; + case Status::FailedToModifyMonitoring: + errorMessage = tr("Failed to modify monitoring"); + break; + case Status::FailedToDisableMonitoring: + errorMessage = tr("Failed to disable monitoring"); + break; + } + } + + if (errorMessage != m_errorMessage) { + m_errorMessage = errorMessage; + emitErrorMessageChanged = true; + } + + if (emitStatusChanged) + emit statusChanged(); + if (emitErrorMessageChanged) + emit errorMessageChanged(); +} + const UniversalNode &OpcUaNode::resolvedNode() const { return m_resolvedNode; @@ -311,8 +538,19 @@ QOpcUa::NodeAttributes OpcUaNode::attributesToRead() const void OpcUaNode::retrieveAbsoluteNodePath(OpcUaNodeIdType *node, std::function<void (const QString &)> functor) { auto conn = connection(); - if (!conn || !m_nodeId || !conn->m_client) { - qCWarning(QT_OPCUA_PLUGINS_QML) << "connection, nodeID or client is invalid"; + if (!conn) { + qCWarning(QT_OPCUA_PLUGINS_QML) << "No connection to server"; + setStatus(Status::NoConnection); + return; + } + if (!m_nodeId) { + qCWarning(QT_OPCUA_PLUGINS_QML) << "Invalid node ID"; + setStatus(Status::InvalidNodeId); + return; + } + if (!conn->m_client) { + qCWarning(QT_OPCUA_PLUGINS_QML) << "Client instance is invalid"; + setStatus(Status::InvalidClient); return; } @@ -336,6 +574,7 @@ void OpcUaNode::retrieveAbsoluteNodePath(OpcUaNodeIdType *node, std::function<vo if (!errorMessage.isEmpty()) { qCWarning(QT_OPCUA_PLUGINS_QML) << "Failed to resolve node:" << errorMessage; + setStatus(Status::FailedToResolveNode, errorMessage); functor(QString()); return; } @@ -353,10 +592,22 @@ void OpcUaNode::retrieveAbsoluteNodePath(OpcUaNodeIdType *node, std::function<vo void OpcUaNode::setReadyToUse(bool value) { + if (value && !checkValidity()) + value = false; + bool old = m_readyToUse; m_readyToUse = value; + + if (value) + setStatus(Status::Valid); + if (!old && value) emit readyToUseChanged(); } +bool OpcUaNode::checkValidity() +{ + return true; +} + QT_END_NAMESPACE |