// NodePage.qml — Demo page displaying a single OPC UA node. import QtQuick import QtQuick.Controls import QtQuick.Layouts import Bobink Page { id: nodePage required property StackView stackRef required property int pageNumber required property var logFunction OpcUaMonitoredNode { id: demoNode nodeId: nodePage.pageNumber === 1 ? "ns=1;s=double_rw_scalar" : "ns=1;s=string_rw_scalar" monitored: nodePage.StackView.status === StackView.Active onMonitoredChanged: nodePage.logFunction("Page " + nodePage.pageNumber + " node [" + nodeId + "] " + (monitored ? "MONITORED" : "UNMONITORED")) onValueChanged: nodePage.logFunction("Page " + nodePage.pageNumber + " value = " + value) } ColumnLayout { anchors.fill: parent anchors.margins: 20 spacing: 8 RowLayout { Label { text: "Node Page " + nodePage.pageNumber font.bold: true font.pointSize: 14 } Item { Layout.fillWidth: true } Button { text: "Disconnect" onClicked: Bobink.disconnectFromServer() } } // Value (prominent) Rectangle { Layout.fillWidth: true height: 60 color: "#f0f0f0" radius: 4 ColumnLayout { anchors.fill: parent anchors.margins: 8 spacing: 2 Label { text: "Value" font.pointSize: 10 color: "gray" } Label { text: demoNode.value !== undefined ? String(demoNode.value) : "—" font.pointSize: 16 font.bold: true elide: Text.ElideRight Layout.fillWidth: true } } } // Node info GridLayout { Layout.fillWidth: true columns: 2 columnSpacing: 12 rowSpacing: 4 Label { text: "Node ID:" color: "gray" } Label { text: demoNode.nodeId Layout.fillWidth: true } Label { text: "Display Name:" color: "gray" } Label { text: demoNode.info.displayName || "—" Layout.fillWidth: true } Label { text: "Data Type:" color: "gray" } Label { text: demoNode.info.dataType || "—" Layout.fillWidth: true } Label { text: "Node Class:" color: "gray" } Label { text: demoNode.info.nodeClass || "—" Layout.fillWidth: true } Label { text: "Access Level:" color: "gray" } Label { text: demoNode.info.accessLevel Layout.fillWidth: true } Label { text: "Status:" color: "gray" } Label { text: demoNode.info.status || "—" Layout.fillWidth: true } Label { text: "Source Time:" color: "gray" } Label { text: demoNode.info.sourceTimestamp.toLocaleString() || "—" Layout.fillWidth: true } Label { text: "Server Time:" color: "gray" } Label { text: demoNode.info.serverTimestamp.toLocaleString() || "—" Layout.fillWidth: true } Label { text: "Monitored:" color: "gray" } Label { text: demoNode.monitored ? "Yes" : "No" color: demoNode.monitored ? "green" : "gray" Layout.fillWidth: true } } Item { Layout.fillHeight: true } Button { Layout.fillWidth: true text: nodePage.pageNumber === 1 ? "Go to Page 2" : "Back to Page 1" onClicked: { if (nodePage.pageNumber === 1) nodePage.stackRef.push("NodePage.qml", { stackRef: nodePage.stackRef, pageNumber: 2, logFunction: nodePage.logFunction }); else nodePage.stackRef.pop(); } } } }