summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorThomas Vanbesien <tvanbesi@proton.me>2026-02-18 00:18:33 +0100
committerThomas Vanbesien <tvanbesi@proton.me>2026-02-18 00:18:33 +0100
commit1a79ab468d8cc23cfdf28ddfa85d3e03ffddf44c (patch)
treee19d73f0aa00958dc65d19cb2dfc607f2469089b /src
parent343169dff6b062074fd3c4a5e240b449ffc4a449 (diff)
downloadBobinkQtOpcUa-1a79ab468d8cc23cfdf28ddfa85d3e03ffddf44c.tar.gz
BobinkQtOpcUa-1a79ab468d8cc23cfdf28ddfa85d3e03ffddf44c.zip
Refactor and document: fix cert filenames, add Doxygen, improve demo
Refactoring (issues 1,3,4,5,7,8,9,10 from review): - Replace hardcoded cert filenames with certFile/keyFile properties - Add 30s timeout to certificate trust QEventLoop - Cache servers() QVariantList instead of rebuilding per call - Validate URLs before passing to QtOpcUa - Use ComboBox valueRole for robust enum mapping - Add certificate trust dialog to demo - Remove unnecessary RowLayout wrapper - Remove debug output from BuildDeps.cmake Documentation: - Add Doxygen file blocks to all C++ files - Document AuthMode enum, toAuthenticationInformation(), key Q_INVOKABLE methods, certificateTrustRequested signal contract - Convert section banners to standard format - Add file/target comments to CMake and QML files
Diffstat (limited to 'src')
-rw-r--r--src/BobinkAuth.cpp4
-rw-r--r--src/BobinkAuth.h65
-rw-r--r--src/BobinkClient.cpp89
-rw-r--r--src/BobinkClient.h45
-rw-r--r--src/CMakeLists.txt1
5 files changed, 158 insertions, 46 deletions
diff --git a/src/BobinkAuth.cpp b/src/BobinkAuth.cpp
index fed1da2..03ae2cf 100644
--- a/src/BobinkAuth.cpp
+++ b/src/BobinkAuth.cpp
@@ -1,3 +1,7 @@
+/**
+ * @file BobinkAuth.cpp
+ * @brief BobinkAuth implementation.
+ */
#include "BobinkAuth.h"
BobinkAuth::BobinkAuth(QObject *parent)
diff --git a/src/BobinkAuth.h b/src/BobinkAuth.h
index 2e3ea6a..2bd3c05 100644
--- a/src/BobinkAuth.h
+++ b/src/BobinkAuth.h
@@ -1,3 +1,7 @@
+/**
+ * @file BobinkAuth.h
+ * @brief QML component for OPC UA authentication configuration.
+ */
#ifndef BOBINKAUTH_H
#define BOBINKAUTH_H
@@ -5,49 +9,60 @@
#include <QOpcUaAuthenticationInformation>
#include <QQmlEngine>
-class BobinkAuth : public QObject {
+class BobinkAuth : public QObject
+{
Q_OBJECT
QML_ELEMENT
- Q_PROPERTY(AuthMode mode READ mode WRITE setMode NOTIFY modeChanged)
- Q_PROPERTY(
+ Q_PROPERTY (AuthMode mode READ mode WRITE setMode NOTIFY modeChanged)
+ Q_PROPERTY (
QString username READ username WRITE setUsername NOTIFY usernameChanged)
- Q_PROPERTY(
+ Q_PROPERTY (
QString password READ password WRITE setPassword NOTIFY passwordChanged)
- Q_PROPERTY(
+ Q_PROPERTY (
QString certPath READ certPath WRITE setCertPath NOTIFY certPathChanged)
- Q_PROPERTY(
+ Q_PROPERTY (
QString keyPath READ keyPath WRITE setKeyPath NOTIFY keyPathChanged)
public:
- enum AuthMode { Anonymous, UserPass, Certificate };
- Q_ENUM(AuthMode)
+ /// Authentication modes supported by OPC UA.
+ enum AuthMode
+ {
+ Anonymous,
+ UserPass,
+ Certificate
+ };
+ Q_ENUM (AuthMode)
- explicit BobinkAuth(QObject *parent = nullptr);
+ explicit BobinkAuth (QObject *parent = nullptr);
- AuthMode mode() const;
- void setMode(AuthMode mode);
+ AuthMode mode () const;
+ void setMode (AuthMode mode);
- QString username() const;
- void setUsername(const QString &username);
+ QString username () const;
+ void setUsername (const QString &username);
- QString password() const;
- void setPassword(const QString &password);
+ QString password () const;
+ void setPassword (const QString &password);
- QString certPath() const;
- void setCertPath(const QString &path);
+ QString certPath () const;
+ void setCertPath (const QString &path);
- QString keyPath() const;
- void setKeyPath(const QString &path);
+ QString keyPath () const;
+ void setKeyPath (const QString &path);
- QOpcUaAuthenticationInformation toAuthenticationInformation() const;
+ /**
+ * @brief Build a QOpcUaAuthenticationInformation from the
+ * current mode and credentials.
+ */
+ QOpcUaAuthenticationInformation toAuthenticationInformation () const;
signals:
- void modeChanged();
- void usernameChanged();
- void passwordChanged();
- void certPathChanged();
- void keyPathChanged();
+ void modeChanged ();
+ void usernameChanged ();
+ void passwordChanged ();
+ void certPathChanged ();
+ void keyPathChanged ();
private:
AuthMode m_mode = Anonymous;
diff --git a/src/BobinkClient.cpp b/src/BobinkClient.cpp
index ea61583..a067489 100644
--- a/src/BobinkClient.cpp
+++ b/src/BobinkClient.cpp
@@ -1,3 +1,7 @@
+/**
+ * @file BobinkClient.cpp
+ * @brief BobinkClient implementation.
+ */
#include "BobinkClient.h"
#include "BobinkAuth.h"
@@ -12,6 +16,7 @@ static QString defaultPkiDir()
+ QStringLiteral("/pki");
}
+/** @brief Create the standard OPC UA PKI directory tree. */
static void ensurePkiDirs(const QString &base)
{
for (const auto *sub : {"own/certs", "own/private",
@@ -69,7 +74,9 @@ void BobinkClient::setupClient()
this, &BobinkClient::handleFindServersFinished);
}
-// --- Connection properties ---
+/* ======================================
+ * Connection properties
+ * ====================================== */
bool BobinkClient::connected() const { return m_connected; }
@@ -95,7 +102,9 @@ void BobinkClient::setAuth(BobinkAuth *auth)
QOpcUaClient *BobinkClient::opcuaClient() const { return m_client; }
-// --- Connection methods ---
+/* ======================================
+ * Connection methods
+ * ====================================== */
void BobinkClient::connectToServer()
{
@@ -112,7 +121,12 @@ void BobinkClient::connectToServer()
return;
}
- m_client->requestEndpoints(QUrl(m_serverUrl));
+ QUrl url(m_serverUrl);
+ if (!url.isValid()) {
+ emit connectionError(QStringLiteral("Invalid server URL: %1").arg(m_serverUrl));
+ return;
+ }
+ m_client->requestEndpoints(url);
}
void BobinkClient::disconnectFromServer()
@@ -135,7 +149,9 @@ void BobinkClient::rejectCertificate()
m_certLoop->quit();
}
-// --- Discovery properties ---
+/* ======================================
+ * Discovery properties
+ * ====================================== */
QString BobinkClient::discoveryUrl() const { return m_discoveryUrl; }
@@ -169,18 +185,12 @@ const QList<QOpcUaApplicationDescription> &BobinkClient::discoveredServers() con
QVariantList BobinkClient::servers() const
{
- QVariantList list;
- for (const auto &s : m_discoveredServers) {
- QVariantMap entry;
- entry[QStringLiteral("serverName")] = s.applicationName().text();
- entry[QStringLiteral("applicationUri")] = s.applicationUri();
- entry[QStringLiteral("discoveryUrls")] = QVariant::fromValue(s.discoveryUrls());
- list.append(entry);
- }
- return list;
+ return m_serversCache;
}
-// --- PKI ---
+/* ======================================
+ * PKI
+ * ====================================== */
QString BobinkClient::pkiDir() const { return m_pkiDir; }
@@ -193,14 +203,36 @@ void BobinkClient::setPkiDir(const QString &path)
emit pkiDirChanged();
}
+QString BobinkClient::certFile() const { return m_certFile; }
+
+void BobinkClient::setCertFile(const QString &path)
+{
+ if (m_certFile == path)
+ return;
+ m_certFile = path;
+ emit certFileChanged();
+}
+
+QString BobinkClient::keyFile() const { return m_keyFile; }
+
+void BobinkClient::setKeyFile(const QString &path)
+{
+ if (m_keyFile == path)
+ return;
+ m_keyFile = path;
+ emit keyFileChanged();
+}
+
void BobinkClient::applyPki()
{
if (!m_client || m_pkiDir.isEmpty())
return;
QOpcUaPkiConfiguration pki;
- pki.setClientCertificateFile(m_pkiDir + QStringLiteral("/own/certs/BobinkDemo_cert.der"));
- pki.setPrivateKeyFile(m_pkiDir + QStringLiteral("/own/private/BobinkDemo_key.pem"));
+ if (!m_certFile.isEmpty())
+ pki.setClientCertificateFile(m_certFile);
+ if (!m_keyFile.isEmpty())
+ pki.setPrivateKeyFile(m_keyFile);
pki.setTrustListDirectory(m_pkiDir + QStringLiteral("/trusted/certs"));
pki.setRevocationListDirectory(m_pkiDir + QStringLiteral("/trusted/crl"));
pki.setIssuerListDirectory(m_pkiDir + QStringLiteral("/issuers/certs"));
@@ -212,7 +244,9 @@ void BobinkClient::applyPki()
m_client->setApplicationIdentity(pki.applicationIdentity());
}
-// --- Discovery methods ---
+/* ======================================
+ * Discovery methods
+ * ====================================== */
void BobinkClient::startDiscovery()
{
@@ -240,11 +274,17 @@ void BobinkClient::stopDiscovery()
void BobinkClient::doDiscovery()
{
- if (m_client && !m_discoveryUrl.isEmpty())
- m_client->findServers(QUrl(m_discoveryUrl));
+ if (!m_client || m_discoveryUrl.isEmpty())
+ return;
+ QUrl url(m_discoveryUrl);
+ if (!url.isValid())
+ return;
+ m_client->findServers(url);
}
-// --- Private slots ---
+/* ======================================
+ * Private slots
+ * ====================================== */
void BobinkClient::handleStateChanged(QOpcUaClient::ClientState state)
{
@@ -291,6 +331,7 @@ void BobinkClient::handleConnectError(QOpcUaErrorState *errorState)
QEventLoop loop;
m_certLoop = &loop;
+ QTimer::singleShot(30000, &loop, &QEventLoop::quit);
loop.exec();
m_certLoop = nullptr;
@@ -311,5 +352,13 @@ void BobinkClient::handleFindServersFinished(
return;
m_discoveredServers = servers;
+ m_serversCache.clear();
+ for (const auto &s : m_discoveredServers) {
+ QVariantMap entry;
+ entry[QStringLiteral("serverName")] = s.applicationName().text();
+ entry[QStringLiteral("applicationUri")] = s.applicationUri();
+ entry[QStringLiteral("discoveryUrls")] = QVariant::fromValue(s.discoveryUrls());
+ m_serversCache.append(entry);
+ }
emit serversChanged();
}
diff --git a/src/BobinkClient.h b/src/BobinkClient.h
index b8e5624..f95ab02 100644
--- a/src/BobinkClient.h
+++ b/src/BobinkClient.h
@@ -1,3 +1,11 @@
+/**
+ * @file BobinkClient.h
+ * @brief QML singleton managing the OPC UA connection lifecycle.
+ *
+ * Wraps QOpcUaClient into a declarative interface: LDS discovery,
+ * endpoint selection, PKI, and certificate trust flow.
+ * Single connection at a time (app-wide singleton).
+ */
#ifndef BOBINKCLIENT_H
#define BOBINKCLIENT_H
@@ -33,12 +41,21 @@ class BobinkClient : public QObject
Q_PROPERTY (QVariantList servers READ servers NOTIFY serversChanged)
Q_PROPERTY (QString pkiDir READ pkiDir WRITE setPkiDir NOTIFY pkiDirChanged)
+ Q_PROPERTY (
+ QString certFile READ certFile WRITE setCertFile NOTIFY certFileChanged)
+ Q_PROPERTY (
+ QString keyFile READ keyFile WRITE setKeyFile NOTIFY keyFileChanged)
public:
explicit BobinkClient (QObject *parent = nullptr);
~BobinkClient () override;
static BobinkClient *instance ();
+
+ /**
+ * @brief QML singleton factory.
+ * @note CppOwnership — lives for the process lifetime.
+ */
static BobinkClient *create (QQmlEngine *qmlEngine, QJSEngine *jsEngine);
bool connected () const;
@@ -65,19 +82,38 @@ public:
QString pkiDir () const;
void setPkiDir (const QString &path);
+ QString certFile () const;
+ void setCertFile (const QString &path);
+
+ QString keyFile () const;
+ void setKeyFile (const QString &path);
+
+ /** @brief Discover endpoints, pick the most secure, connect. */
Q_INVOKABLE void connectToServer ();
Q_INVOKABLE void disconnectFromServer ();
+
+ /** @brief Accept the pending server certificate. */
Q_INVOKABLE void acceptCertificate ();
+ /** @brief Reject the pending server certificate. */
Q_INVOKABLE void rejectCertificate ();
Q_INVOKABLE void startDiscovery ();
Q_INVOKABLE void stopDiscovery ();
+
+ /** @brief Apply PKI dirs and cert/key. Call before connecting. */
Q_INVOKABLE void applyPki ();
signals:
void connectedChanged ();
void serverUrlChanged ();
void authChanged ();
+
+ /**
+ * @brief Emitted when the server presents an untrusted cert.
+ *
+ * The connection blocks until acceptCertificate() or
+ * rejectCertificate() is called (30 s timeout, auto-rejects).
+ */
void certificateTrustRequested (const QString &certInfo);
void connectionError (const QString &message);
@@ -86,6 +122,8 @@ signals:
void discoveringChanged ();
void serversChanged ();
void pkiDirChanged ();
+ void certFileChanged ();
+ void keyFileChanged ();
private slots:
void handleStateChanged (QOpcUaClient::ClientState state);
@@ -108,16 +146,21 @@ private:
BobinkAuth *m_auth = nullptr;
QString m_serverUrl;
bool m_connected = false;
+
+ // Certificate trust event loop — see handleConnectError().
QEventLoop *m_certLoop = nullptr;
bool m_certAccepted = false;
QString m_discoveryUrl;
- int m_discoveryInterval = 10000;
+ int m_discoveryInterval = 10000; // ms
QTimer m_discoveryTimer;
bool m_discovering = false;
QList<QOpcUaApplicationDescription> m_discoveredServers;
QString m_pkiDir;
+ QString m_certFile;
+ QString m_keyFile;
+ QVariantList m_serversCache;
};
#endif // BOBINKCLIENT_H
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 9b7cac2..2f10c7a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,3 +1,4 @@
+# bobink — QML module wrapping QtOpcUa for declarative use.
qt_add_qml_module(bobink
URI Bobink
VERSION 1.0