From 343169dff6b062074fd3c4a5e240b449ffc4a449 Mon Sep 17 00:00:00 2001 From: Thomas Vanbesien Date: Tue, 17 Feb 2026 23:58:08 +0100 Subject: Initial Bobink library: BobinkAuth, BobinkClient, and demo app Implements the core OPC UA wrapper library with: - Build system with automatic dep building (open62541, QtOpcUa) - BobinkAuth: QML auth component (anonymous/userpass/certificate) - BobinkClient: QML singleton managing connection, LDS discovery, PKI configuration, endpoint selection, and certificate trust flow - Demo app for manual testing of the full connection flow --- demo/Main.qml | 188 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 demo/Main.qml (limited to 'demo/Main.qml') diff --git a/demo/Main.qml b/demo/Main.qml new file mode 100644 index 0000000..6d1327d --- /dev/null +++ b/demo/Main.qml @@ -0,0 +1,188 @@ +import QtQuick +import QtQuick.Controls +import QtQuick.Layouts +import Bobink + +ApplicationWindow { + width: 600 + height: 600 + visible: true + title: "Bobink Demo" + + Connections { + target: BobinkClient + function onServersChanged() { + console.log("Discovered server list updated") + } + function onConnectedChanged() { + console.log("Connected:", BobinkClient.connected) + } + function onConnectionError(message) { + console.log("Connection error:", message) + } + function onDiscoveringChanged() { + console.log("Discovering:", BobinkClient.discovering) + } + } + + ColumnLayout { + anchors.fill: parent + anchors.margins: 20 + spacing: 12 + + Label { text: "Discovery URL"; font.bold: true } + + RowLayout { + TextField { + id: discoveryUrlField + Layout.fillWidth: true + text: "opc.tcp://localhost:4840" + } + Button { + text: BobinkClient.discovering ? "Stop" : "Discover" + onClicked: { + if (BobinkClient.discovering) { + BobinkClient.stopDiscovery() + } else { + BobinkClient.discoveryUrl = discoveryUrlField.text + BobinkClient.startDiscovery() + } + } + } + } + + Label { + text: BobinkClient.discovering + ? "Discovering... (" + BobinkClient.servers.length + " found)" + : BobinkClient.servers.length + " server(s)" + font.italic: true + } + + ListView { + Layout.fillWidth: true + Layout.preferredHeight: 100 + clip: true + model: BobinkClient.servers + delegate: ItemDelegate { + width: ListView.view.width + text: modelData.serverName + " — " + modelData.applicationUri + onClicked: { + if (modelData.discoveryUrls.length > 0) + serverUrlField.text = modelData.discoveryUrls[0] + } + } + ScrollBar.vertical: ScrollBar {} + } + + Label { text: "PKI Directory"; font.bold: true } + + RowLayout { + TextField { + id: pkiDirField + Layout.fillWidth: true + text: BobinkClient.pkiDir + onEditingFinished: BobinkClient.pkiDir = text + } + Button { + text: "Apply PKI" + onClicked: { + BobinkClient.pkiDir = pkiDirField.text + BobinkClient.applyPki() + console.log("PKI applied:", BobinkClient.pkiDir) + } + } + } + + Label { text: "Server URL"; font.bold: true } + + RowLayout { + TextField { + id: serverUrlField + Layout.fillWidth: true + placeholderText: "opc.tcp://..." + } + } + + Label { text: "Authentication"; font.bold: true } + + BobinkAuth { + id: auth + mode: { + switch (authModeCombo.currentIndex) { + case 0: return BobinkAuth.Anonymous + case 1: return BobinkAuth.UserPass + case 2: return BobinkAuth.Certificate + default: return BobinkAuth.Anonymous + } + } + username: usernameField.text + password: passwordField.text + certPath: certPathField.text + keyPath: keyPathField.text + } + + ComboBox { + id: authModeCombo + Layout.fillWidth: true + model: ["Anonymous", "Username / Password", "Certificate"] + } + + GridLayout { + columns: 2 + visible: authModeCombo.currentIndex === 1 + Layout.fillWidth: true + + Label { text: "Username:" } + TextField { + id: usernameField + Layout.fillWidth: true + } + Label { text: "Password:" } + TextField { + id: passwordField + Layout.fillWidth: true + echoMode: TextInput.Password + } + } + + GridLayout { + columns: 2 + visible: authModeCombo.currentIndex === 2 + Layout.fillWidth: true + + Label { text: "Certificate:" } + TextField { + id: certPathField + Layout.fillWidth: true + placeholderText: "/path/to/cert.pem" + } + Label { text: "Private key:" } + TextField { + id: keyPathField + Layout.fillWidth: true + placeholderText: "/path/to/key.pem" + } + } + + Button { + text: BobinkClient.connected ? "Disconnect" : "Connect" + Layout.fillWidth: true + onClicked: { + if (BobinkClient.connected) { + BobinkClient.disconnectFromServer() + } else { + BobinkClient.auth = auth + BobinkClient.serverUrl = serverUrlField.text + BobinkClient.connectToServer() + } + } + } + + Label { + text: "Status: " + (BobinkClient.connected ? "Connected" : "Disconnected") + color: BobinkClient.connected ? "green" : "red" + } + + Item { Layout.fillHeight: true } + } +} -- cgit v1.2.3