diff options
| author | Thomas Vanbesien <tvanbesi@proton.me> | 2026-02-18 00:18:33 +0100 |
|---|---|---|
| committer | Thomas Vanbesien <tvanbesi@proton.me> | 2026-02-18 00:18:33 +0100 |
| commit | 1a79ab468d8cc23cfdf28ddfa85d3e03ffddf44c (patch) | |
| tree | e19d73f0aa00958dc65d19cb2dfc607f2469089b /src | |
| parent | 343169dff6b062074fd3c4a5e240b449ffc4a449 (diff) | |
| download | BobinkQtOpcUa-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.cpp | 4 | ||||
| -rw-r--r-- | src/BobinkAuth.h | 65 | ||||
| -rw-r--r-- | src/BobinkClient.cpp | 89 | ||||
| -rw-r--r-- | src/BobinkClient.h | 45 | ||||
| -rw-r--r-- | src/CMakeLists.txt | 1 |
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 |
