aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--demo/NodePage.qml41
-rw-r--r--src/OpcUaMonitoredNode.cpp67
-rw-r--r--src/OpcUaMonitoredNode.h13
3 files changed, 118 insertions, 3 deletions
diff --git a/demo/NodePage.qml b/demo/NodePage.qml
index e677232..9b23502 100644
--- a/demo/NodePage.qml
+++ b/demo/NodePage.qml
@@ -91,6 +91,19 @@ Page {
]
},
{
+ title: "Index Range Write",
+ description: "Write to specific array elements using OPC UA index"
+ + " range syntax. Examples: \"0\" = first element,"
+ + " \"0:2\" = elements 0–2. Enter the range and the value(s)"
+ + " to write (comma-separated for multi-element ranges).",
+ indexRange: true,
+ nodes: [
+ "ns=1;s=int32_rw_array",
+ "ns=1;s=float_rw_array",
+ "ns=1;s=string_rw_array"
+ ]
+ },
+ {
title: "Non-Existent Nodes",
description: "These node IDs do not exist on the server."
+ " The row should show no value and no metadata in the tooltip.",
@@ -243,19 +256,41 @@ Page {
}
// Column 3: Edit area (writable) or READ-ONLY label
+
+ // Normal write controls (non-index-range pages)
TextField {
id: editField
- visible: node.writable
+ visible: node.writable && !currentPage.indexRange
Layout.fillWidth: true
placeholderText: "Enter value..."
onAccepted: node.writeValue(text)
}
Button {
- id: writeButton
- visible: node.writable
+ visible: node.writable && !currentPage.indexRange
text: "Write"
onClicked: node.writeValue(editField.text)
}
+
+ // Index range write controls
+ TextField {
+ id: rangeField
+ visible: node.writable && currentPage.indexRange === true
+ Layout.preferredWidth: 60
+ placeholderText: "Range"
+ }
+ TextField {
+ id: rangeValueField
+ visible: node.writable && currentPage.indexRange === true
+ Layout.fillWidth: true
+ placeholderText: "Value(s)..."
+ onAccepted: node.writeValueAtRange(text, rangeField.text)
+ }
+ Button {
+ visible: node.writable && currentPage.indexRange === true
+ text: "Write Range"
+ onClicked: node.writeValueAtRange(rangeValueField.text, rangeField.text)
+ }
+
Label {
visible: !node.writable
text: "(READ-ONLY)"
diff --git a/src/OpcUaMonitoredNode.cpp b/src/OpcUaMonitoredNode.cpp
index cd93730..922ad43 100644
--- a/src/OpcUaMonitoredNode.cpp
+++ b/src/OpcUaMonitoredNode.cpp
@@ -177,6 +177,7 @@ OpcUaMonitoredNode::teardownNode ()
emit infoChanged ();
m_valueType = QOpcUa::Types::Undefined;
+ m_pendingRangeWrite = false;
if (m_writable)
{
@@ -270,6 +271,9 @@ OpcUaMonitoredNode::handleAttributeUpdated (QOpcUa::NodeAttribute attr,
void
OpcUaMonitoredNode::handleValueUpdated (const QVariant &value)
{
+ if (m_pendingRangeWrite)
+ return;
+
m_value = value;
emit valueChanged ();
@@ -306,6 +310,8 @@ OpcUaMonitoredNode::handleAttributeWritten (QOpcUa::NodeAttribute attr,
if (attr != QOpcUa::NodeAttribute::Value)
return;
+ m_pendingRangeWrite = false;
+
bool ok = (statusCode == QOpcUa::Good);
emit writeCompleted (ok, ok ? QStringLiteral ("Write successful")
: QOpcUa::statusToString (statusCode));
@@ -511,3 +517,64 @@ OpcUaMonitoredNode::writeValue (const QVariant &value)
emit writeCompleted (false,
QStringLiteral ("Write request failed to dispatch"));
}
+
+void
+OpcUaMonitoredNode::writeValueAtRange (const QVariant &value,
+ const QString &indexRange)
+{
+ if (!m_node)
+ {
+ emit writeCompleted (false, QStringLiteral ("Node not connected"));
+ return;
+ }
+ if (!m_writable)
+ {
+ emit writeCompleted (false, QStringLiteral ("Node is read-only"));
+ return;
+ }
+ if (m_valueType == QOpcUa::Types::Undefined)
+ {
+ emit writeCompleted (false,
+ QStringLiteral ("Data type not yet resolved"));
+ return;
+ }
+ if (indexRange.isEmpty ())
+ {
+ emit writeCompleted (false, QStringLiteral ("Index range is empty"));
+ return;
+ }
+
+ QVariant toWrite = value;
+
+ // If the input is a comma-separated string, split into a list so
+ // coerceValue can handle each element (e.g. "10, 20" for range "0:1").
+ if (value.metaType ().id () == QMetaType::QString
+ && value.toString ().contains (QLatin1Char (',')))
+ {
+ QStringList parts
+ = value.toString ().split (QLatin1Char (','), Qt::SkipEmptyParts);
+ QVariantList list;
+ list.reserve (parts.size ());
+ for (const QString &part : parts)
+ list.append (QVariant (part.trimmed ()));
+ toWrite = QVariant::fromValue (list);
+ }
+
+ QVariant coerced = coerceValue (toWrite);
+ if (!coerced.isValid ())
+ {
+ emit writeCompleted (
+ false, QStringLiteral ("Cannot convert value to node data type"));
+ return;
+ }
+
+ m_pendingRangeWrite = true;
+
+ if (!m_node->writeAttributeRange (QOpcUa::NodeAttribute::Value, coerced,
+ indexRange, m_valueType))
+ {
+ m_pendingRangeWrite = false;
+ emit writeCompleted (
+ false, QStringLiteral ("Write range request failed to dispatch"));
+ }
+}
diff --git a/src/OpcUaMonitoredNode.h b/src/OpcUaMonitoredNode.h
index 2e86b2c..2ecffb7 100644
--- a/src/OpcUaMonitoredNode.h
+++ b/src/OpcUaMonitoredNode.h
@@ -88,6 +88,18 @@ public:
*/
Q_INVOKABLE void writeValue (const QVariant &value);
+ /**
+ * @brief Write a value to a specific index range of an array node.
+ *
+ * @a indexRange uses OPC UA syntax: "0" for first element,
+ * "0:3" for elements 0–3, "0,1" for 2D indexing.
+ * For multi-element writes, @a value can be a comma-separated
+ * string or a QVariantList.
+ * Result is reported asynchronously via writeCompleted().
+ */
+ Q_INVOKABLE void writeValueAtRange (const QVariant &value,
+ const QString &indexRange);
+
void classBegin () override;
void componentComplete () override;
@@ -129,6 +141,7 @@ private:
bool m_writable = false;
QVariant m_value;
OpcUaNodeInfo m_info;
+ bool m_pendingRangeWrite = false;
};
#endif // OPCUAMONITOREDNODE_H