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 } } }