summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/BobinkClient.cpp685
-rw-r--r--src/BobinkClient.h9
-rw-r--r--src/CMakeLists.txt27
3 files changed, 434 insertions, 287 deletions
diff --git a/src/BobinkClient.cpp b/src/BobinkClient.cpp
index 565868c..e7f6d0d 100644
--- a/src/BobinkClient.cpp
+++ b/src/BobinkClient.cpp
@@ -11,433 +11,582 @@
BobinkClient *BobinkClient::s_instance = nullptr;
-static QString defaultPkiDir()
+static QString
+defaultPkiDir ()
{
- return QStandardPaths::writableLocation(QStandardPaths::AppDataLocation)
- + QStringLiteral("/pki");
+ return QStandardPaths::writableLocation (QStandardPaths::AppDataLocation)
+ + QStringLiteral ("/pki");
}
/** @brief Create the standard OPC UA PKI directory tree. */
-static void ensurePkiDirs(const QString &base)
+static void
+ensurePkiDirs (const QString &base)
{
- for (const auto *sub : {"own/certs", "own/private",
- "trusted/certs", "trusted/crl",
- "issuers/certs", "issuers/crl"}) {
- QDir().mkpath(base + QLatin1Char('/') + QLatin1String(sub));
+ for (const auto *sub : { "own/certs", "own/private", "trusted/certs",
+ "trusted/crl", "issuers/certs", "issuers/crl" })
+ {
+ QDir ().mkpath (base + QLatin1Char ('/') + QLatin1String (sub));
}
}
-BobinkClient::BobinkClient(QObject *parent)
- : QObject(parent)
- , m_provider(new QOpcUaProvider(this))
- , m_pkiDir(defaultPkiDir())
+BobinkClient::BobinkClient (QObject *parent)
+ : QObject (parent), m_provider (new QOpcUaProvider (this)),
+ m_pkiDir (defaultPkiDir ())
{
- ensurePkiDirs(m_pkiDir);
- setupClient();
- autoDetectPki();
- applyPki();
- connect(&m_discoveryTimer, &QTimer::timeout, this, &BobinkClient::doDiscovery);
+ ensurePkiDirs (m_pkiDir);
+ setupClient ();
+ autoDetectPki ();
+ applyPki ();
+ connect (&m_discoveryTimer, &QTimer::timeout, this,
+ &BobinkClient::doDiscovery);
}
-BobinkClient::~BobinkClient()
+BobinkClient::~BobinkClient ()
{
- if (s_instance == this)
- s_instance = nullptr;
+ if (s_instance == this)
+ s_instance = nullptr;
}
-BobinkClient *BobinkClient::instance()
+BobinkClient *
+BobinkClient::instance ()
{
- return s_instance;
+ return s_instance;
}
-BobinkClient *BobinkClient::create(QQmlEngine *, QJSEngine *)
+BobinkClient *
+BobinkClient::create (QQmlEngine *, QJSEngine *)
{
- if (!s_instance) {
- s_instance = new BobinkClient;
- QJSEngine::setObjectOwnership(s_instance, QJSEngine::CppOwnership);
+ if (!s_instance)
+ {
+ s_instance = new BobinkClient;
+ QJSEngine::setObjectOwnership (s_instance, QJSEngine::CppOwnership);
}
- return s_instance;
+ return s_instance;
}
-void BobinkClient::setupClient()
+void
+BobinkClient::setupClient ()
{
- m_client = m_provider->createClient(QStringLiteral("open62541"));
- if (!m_client) {
- qWarning() << "BobinkClient: failed to create open62541 backend";
- return;
+ m_client = m_provider->createClient (QStringLiteral ("open62541"));
+ if (!m_client)
+ {
+ qWarning () << "BobinkClient: failed to create open62541 backend";
+ return;
}
- connect(m_client, &QOpcUaClient::stateChanged,
- this, &BobinkClient::handleStateChanged);
- connect(m_client, &QOpcUaClient::endpointsRequestFinished,
- this, &BobinkClient::handleEndpointsReceived);
- connect(m_client, &QOpcUaClient::connectError,
- this, &BobinkClient::handleConnectError);
- connect(m_client, &QOpcUaClient::findServersFinished,
- this, &BobinkClient::handleFindServersFinished);
+ connect (m_client, &QOpcUaClient::stateChanged, this,
+ &BobinkClient::handleStateChanged);
+ connect (m_client, &QOpcUaClient::endpointsRequestFinished, this,
+ &BobinkClient::handleEndpointsReceived);
+ connect (m_client, &QOpcUaClient::connectError, this,
+ &BobinkClient::handleConnectError);
+ connect (m_client, &QOpcUaClient::findServersFinished, this,
+ &BobinkClient::handleFindServersFinished);
+ connect (m_client, &QOpcUaClient::errorChanged, this,
+ [this] (QOpcUaClient::ClientError error)
+ {
+ static const char *names[]
+ = { "NoError",
+ "InvalidUrl",
+ "AccessDenied",
+ "ConnectionError",
+ "UnknownError",
+ "UnsupportedAuthenticationInformation",
+ "InvalidAuthenticationInformation",
+ "InvalidEndpointDescription",
+ "NoMatchingUserIdentityTokenFound",
+ "UnsupportedSecurityPolicy",
+ "InvalidPki" };
+ int idx = static_cast<int> (error);
+ if (idx > 0 && idx <= 10)
+ emit connectionError (
+ QStringLiteral ("Client error: %1").arg (names[idx]));
+ });
}
/* ======================================
* Connection properties
* ====================================== */
-bool BobinkClient::connected() const { return m_connected; }
+bool
+BobinkClient::connected () const
+{
+ return m_connected;
+}
-QString BobinkClient::serverUrl() const { return m_serverUrl; }
+QString
+BobinkClient::serverUrl () const
+{
+ return m_serverUrl;
+}
-void BobinkClient::setServerUrl(const QString &url)
+void
+BobinkClient::setServerUrl (const QString &url)
{
- if (m_serverUrl == url)
- return;
- m_serverUrl = url;
- emit serverUrlChanged();
+ if (m_serverUrl == url)
+ return;
+ m_serverUrl = url;
+ emit serverUrlChanged ();
}
-BobinkAuth *BobinkClient::auth() const { return m_auth; }
+BobinkAuth *
+BobinkClient::auth () const
+{
+ return m_auth;
+}
-void BobinkClient::setAuth(BobinkAuth *auth)
+void
+BobinkClient::setAuth (BobinkAuth *auth)
{
- if (m_auth == auth)
- return;
- m_auth = auth;
- emit authChanged();
+ if (m_auth == auth)
+ return;
+ m_auth = auth;
+ emit authChanged ();
}
-QOpcUaClient *BobinkClient::opcuaClient() const { return m_client; }
+QOpcUaClient *
+BobinkClient::opcuaClient () const
+{
+ return m_client;
+}
/* ======================================
* Connection methods
* ====================================== */
-void BobinkClient::connectToServer()
+void
+BobinkClient::connectToServer ()
{
- if (!m_client) {
- emit connectionError(QStringLiteral("OPC UA backend not available"));
- return;
+ if (!m_client)
+ {
+ emit connectionError (QStringLiteral ("OPC UA backend not available"));
+ return;
}
- if (m_serverUrl.isEmpty()) {
- emit connectionError(QStringLiteral("No server URL set"));
- return;
+ if (m_serverUrl.isEmpty ())
+ {
+ emit connectionError (QStringLiteral ("No server URL set"));
+ return;
}
- if (m_client->state() != QOpcUaClient::Disconnected) {
- emit connectionError(QStringLiteral("Already connected or connecting"));
- return;
+ if (m_client->state () != QOpcUaClient::Disconnected)
+ {
+ emit connectionError (
+ QStringLiteral ("Already connected or connecting"));
+ return;
}
- QUrl url(m_serverUrl);
- if (!url.isValid()) {
- emit connectionError(QStringLiteral("Invalid server URL: %1").arg(m_serverUrl));
- return;
+ QUrl url (m_serverUrl);
+ if (!url.isValid ())
+ {
+ emit connectionError (
+ QStringLiteral ("Invalid server URL: %1").arg (m_serverUrl));
+ return;
}
- m_client->requestEndpoints(url);
+ m_client->requestEndpoints (url);
}
-static QString securityPolicyUri(BobinkClient::SecurityPolicy policy)
+static QString
+securityPolicyUri (BobinkClient::SecurityPolicy policy)
{
- switch (policy) {
+ switch (policy)
+ {
case BobinkClient::Basic256Sha256:
- return QStringLiteral(
- "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
+ return QStringLiteral (
+ "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256");
case BobinkClient::Aes128_Sha256_RsaOaep:
- return QStringLiteral(
- "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
+ return QStringLiteral (
+ "http://opcfoundation.org/UA/SecurityPolicy#Aes128_Sha256_RsaOaep");
case BobinkClient::Aes256_Sha256_RsaPss:
- return QStringLiteral(
- "http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
+ return QStringLiteral (
+ "http://opcfoundation.org/UA/SecurityPolicy#Aes256_Sha256_RsaPss");
}
- return {};
+ return {};
}
-void BobinkClient::connectDirect(SecurityPolicy policy, SecurityMode mode)
+void
+BobinkClient::connectDirect (SecurityPolicy policy, SecurityMode mode)
{
- if (!m_client) {
- emit connectionError(QStringLiteral("OPC UA backend not available"));
- return;
+ if (!m_client)
+ {
+ emit connectionError (QStringLiteral ("OPC UA backend not available"));
+ return;
}
- if (m_serverUrl.isEmpty()) {
- emit connectionError(QStringLiteral("No server URL set"));
- return;
+ if (m_serverUrl.isEmpty ())
+ {
+ emit connectionError (QStringLiteral ("No server URL set"));
+ return;
}
- if (m_client->state() != QOpcUaClient::Disconnected) {
- emit connectionError(QStringLiteral("Already connected or connecting"));
- return;
+ if (m_client->state () != QOpcUaClient::Disconnected)
+ {
+ emit connectionError (
+ QStringLiteral ("Already connected or connecting"));
+ return;
}
- QOpcUaEndpointDescription endpoint;
- endpoint.setEndpointUrl(m_serverUrl);
- endpoint.setSecurityPolicy(securityPolicyUri(policy));
- endpoint.setSecurityMode(
- static_cast<QOpcUaEndpointDescription::MessageSecurityMode>(mode));
-
- QOpcUaUserTokenPolicy tokenPolicy;
- if (m_auth) {
- switch (m_auth->mode()) {
+ QOpcUaEndpointDescription endpoint;
+ endpoint.setEndpointUrl (m_serverUrl);
+ endpoint.setSecurityPolicy (securityPolicyUri (policy));
+ endpoint.setSecurityMode (
+ static_cast<QOpcUaEndpointDescription::MessageSecurityMode> (mode));
+
+ QOpcUaUserTokenPolicy tokenPolicy;
+ if (m_auth)
+ {
+ switch (m_auth->mode ())
+ {
case BobinkAuth::Anonymous:
- tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Anonymous);
- break;
+ tokenPolicy.setTokenType (
+ QOpcUaUserTokenPolicy::TokenType::Anonymous);
+ break;
case BobinkAuth::UserPass:
- tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Username);
- break;
+ tokenPolicy.setTokenType (
+ QOpcUaUserTokenPolicy::TokenType::Username);
+ break;
case BobinkAuth::Certificate:
- tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Certificate);
- break;
+ tokenPolicy.setTokenType (
+ QOpcUaUserTokenPolicy::TokenType::Certificate);
+ break;
}
- m_client->setAuthenticationInformation(m_auth->toAuthenticationInformation());
- } else {
- tokenPolicy.setTokenType(QOpcUaUserTokenPolicy::TokenType::Anonymous);
+ m_client->setAuthenticationInformation (
+ m_auth->toAuthenticationInformation ());
+ }
+ else
+ {
+ tokenPolicy.setTokenType (QOpcUaUserTokenPolicy::TokenType::Anonymous);
}
- endpoint.setUserIdentityTokens({tokenPolicy});
+ endpoint.setUserIdentityTokens ({ tokenPolicy });
- m_client->connectToEndpoint(endpoint);
+ m_client->connectToEndpoint (endpoint);
}
-void BobinkClient::disconnectFromServer()
+void
+BobinkClient::disconnectFromServer ()
{
- if (m_client)
- m_client->disconnectFromEndpoint();
+ if (m_client)
+ m_client->disconnectFromEndpoint ();
}
-void BobinkClient::acceptCertificate()
+void
+BobinkClient::acceptCertificate ()
{
- m_certAccepted = true;
- if (m_certLoop)
- m_certLoop->quit();
+ m_certAccepted = true;
+ if (m_certLoop)
+ m_certLoop->quit ();
}
-void BobinkClient::rejectCertificate()
+void
+BobinkClient::rejectCertificate ()
{
- m_certAccepted = false;
- if (m_certLoop)
- m_certLoop->quit();
+ m_certAccepted = false;
+ if (m_certLoop)
+ m_certLoop->quit ();
}
/* ======================================
* Discovery properties
* ====================================== */
-QString BobinkClient::discoveryUrl() const { return m_discoveryUrl; }
-
-void BobinkClient::setDiscoveryUrl(const QString &url)
+QString
+BobinkClient::discoveryUrl () const
{
- if (m_discoveryUrl == url)
- return;
- m_discoveryUrl = url;
- emit discoveryUrlChanged();
+ return m_discoveryUrl;
}
-int BobinkClient::discoveryInterval() const { return m_discoveryInterval; }
-
-void BobinkClient::setDiscoveryInterval(int ms)
+void
+BobinkClient::setDiscoveryUrl (const QString &url)
{
- if (m_discoveryInterval == ms)
- return;
- m_discoveryInterval = ms;
- emit discoveryIntervalChanged();
-
- if (m_discoveryTimer.isActive())
- m_discoveryTimer.setInterval(ms);
+ if (m_discoveryUrl == url)
+ return;
+ m_discoveryUrl = url;
+ emit discoveryUrlChanged ();
}
-bool BobinkClient::discovering() const { return m_discovering; }
+bool
+BobinkClient::discovering () const
+{
+ return m_discovering;
+}
-const QList<QOpcUaApplicationDescription> &BobinkClient::discoveredServers() const
+const QList<QOpcUaApplicationDescription> &
+BobinkClient::discoveredServers () const
{
- return m_discoveredServers;
+ return m_discoveredServers;
}
-QVariantList BobinkClient::servers() const
+QVariantList
+BobinkClient::servers () const
{
- return m_serversCache;
+ return m_serversCache;
}
/* ======================================
* PKI
* ====================================== */
-QString BobinkClient::pkiDir() const { return m_pkiDir; }
-
-void BobinkClient::setPkiDir(const QString &path)
+QString
+BobinkClient::pkiDir () const
{
- if (m_pkiDir == path)
- return;
- m_pkiDir = path;
- ensurePkiDirs(m_pkiDir);
- emit pkiDirChanged();
+ return m_pkiDir;
}
-QString BobinkClient::certFile() const { return m_certFile; }
-
-void BobinkClient::setCertFile(const QString &path)
+void
+BobinkClient::setPkiDir (const QString &path)
{
- if (m_certFile == path)
- return;
- m_certFile = path;
- emit certFileChanged();
+ if (m_pkiDir == path)
+ return;
+ m_pkiDir = path;
+ ensurePkiDirs (m_pkiDir);
+ emit pkiDirChanged ();
}
-QString BobinkClient::keyFile() const { return m_keyFile; }
-
-void BobinkClient::setKeyFile(const QString &path)
+QString
+BobinkClient::certFile () const
{
- if (m_keyFile == path)
- return;
- m_keyFile = path;
- emit keyFileChanged();
+ return m_certFile;
}
-void BobinkClient::autoDetectPki()
+void
+BobinkClient::setCertFile (const QString &path)
{
- if (m_pkiDir.isEmpty())
- return;
-
- QDir certDir(m_pkiDir + QStringLiteral("/own/certs"));
- QStringList certs = certDir.entryList({QStringLiteral("*.der")}, QDir::Files);
- if (!certs.isEmpty())
- setCertFile(certDir.filePath(certs.first()));
+ if (m_certFile == path)
+ return;
+ m_certFile = path;
+ emit certFileChanged ();
+}
- QDir keyDir(m_pkiDir + QStringLiteral("/own/private"));
- QStringList keys = keyDir.entryList(
- {QStringLiteral("*.pem"), QStringLiteral("*.crt")}, QDir::Files);
- if (!keys.isEmpty())
- setKeyFile(keyDir.filePath(keys.first()));
+QString
+BobinkClient::keyFile () const
+{
+ return m_keyFile;
}
-void BobinkClient::applyPki()
+void
+BobinkClient::setKeyFile (const QString &path)
{
- if (!m_client || m_pkiDir.isEmpty())
- return;
+ if (m_keyFile == path)
+ return;
+ m_keyFile = path;
+ emit keyFileChanged ();
+}
- QOpcUaPkiConfiguration pki;
- 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"));
- pki.setIssuerRevocationListDirectory(m_pkiDir + QStringLiteral("/issuers/crl"));
+void
+BobinkClient::autoDetectPki ()
+{
+ if (m_pkiDir.isEmpty ())
+ return;
+
+ QDir certDir (m_pkiDir + QStringLiteral ("/own/certs"));
+ QStringList certs
+ = certDir.entryList ({ QStringLiteral ("*.der") }, QDir::Files);
+ if (!certs.isEmpty ())
+ setCertFile (certDir.filePath (certs.first ()));
+
+ QDir keyDir (m_pkiDir + QStringLiteral ("/own/private"));
+ QStringList keys = keyDir.entryList (
+ { QStringLiteral ("*.pem"), QStringLiteral ("*.crt") }, QDir::Files);
+ if (!keys.isEmpty ())
+ setKeyFile (keyDir.filePath (keys.first ()));
+}
- m_client->setPkiConfiguration(pki);
+void
+BobinkClient::applyPki ()
+{
+ if (!m_client || m_pkiDir.isEmpty ())
+ return;
+
+ if (!m_certFile.isEmpty () && !QFile::exists (m_certFile))
+ {
+ emit statusMessage (
+ QStringLiteral ("PKI error: certificate not found: %1")
+ .arg (m_certFile));
+ return;
+ }
+ if (!m_keyFile.isEmpty () && !QFile::exists (m_keyFile))
+ {
+ emit statusMessage (
+ QStringLiteral ("PKI error: private key not found: %1")
+ .arg (m_keyFile));
+ return;
+ }
+ if (!m_certFile.isEmpty () && m_keyFile.isEmpty ())
+ {
+ emit statusMessage (
+ QStringLiteral ("PKI error: certificate set but no private key"));
+ return;
+ }
+ if (m_certFile.isEmpty () && !m_keyFile.isEmpty ())
+ {
+ emit statusMessage (
+ QStringLiteral ("PKI error: private key set but no certificate"));
+ return;
+ }
- if (pki.isKeyAndCertificateFileSet())
- m_client->setApplicationIdentity(pki.applicationIdentity());
+ QOpcUaPkiConfiguration pki;
+ 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"));
+ pki.setIssuerRevocationListDirectory (m_pkiDir
+ + QStringLiteral ("/issuers/crl"));
+
+ m_client->setPkiConfiguration (pki);
+
+ if (pki.isKeyAndCertificateFileSet ())
+ {
+ auto identity = pki.applicationIdentity ();
+ if (!identity.isValid ())
+ {
+ emit statusMessage (QStringLiteral (
+ "PKI error: certificate could not be parsed (invalid DER?)"));
+ return;
+ }
+ m_client->setApplicationIdentity (identity);
+ emit statusMessage (QStringLiteral ("PKI applied: %1")
+ .arg (m_certFile.split ('/').last ()));
+ }
+ else
+ {
+ emit statusMessage (
+ QStringLiteral ("PKI applied (no client certificate)"));
+ }
}
/* ======================================
* Discovery methods
* ====================================== */
-void BobinkClient::startDiscovery()
+void
+BobinkClient::startDiscovery ()
{
- if (m_discoveryUrl.isEmpty() || !m_client)
- return;
+ if (m_discoveryUrl.isEmpty () || !m_client)
+ return;
- doDiscovery();
- m_discoveryTimer.start(m_discoveryInterval);
+ doDiscovery ();
+ m_discoveryTimer.start (m_discoveryInterval);
- if (!m_discovering) {
- m_discovering = true;
- emit discoveringChanged();
+ if (!m_discovering)
+ {
+ m_discovering = true;
+ emit discoveringChanged ();
}
}
-void BobinkClient::stopDiscovery()
+void
+BobinkClient::stopDiscovery ()
{
- m_discoveryTimer.stop();
+ m_discoveryTimer.stop ();
- if (m_discovering) {
- m_discovering = false;
- emit discoveringChanged();
+ if (m_discovering)
+ {
+ m_discovering = false;
+ emit discoveringChanged ();
}
}
-void BobinkClient::doDiscovery()
+void
+BobinkClient::doDiscovery ()
{
- if (!m_client || m_discoveryUrl.isEmpty())
- return;
- QUrl url(m_discoveryUrl);
- if (!url.isValid())
- return;
- m_client->findServers(url);
+ if (!m_client || m_discoveryUrl.isEmpty ())
+ return;
+ QUrl url (m_discoveryUrl);
+ if (!url.isValid ())
+ return;
+ m_client->findServers (url);
}
/* ======================================
* Private slots
* ====================================== */
-void BobinkClient::handleStateChanged(QOpcUaClient::ClientState state)
+void
+BobinkClient::handleStateChanged (QOpcUaClient::ClientState state)
{
- bool nowConnected = (state == QOpcUaClient::Connected);
- if (m_connected != nowConnected) {
- m_connected = nowConnected;
- emit connectedChanged();
+ bool nowConnected = (state == QOpcUaClient::Connected);
+ if (m_connected != nowConnected)
+ {
+ m_connected = nowConnected;
+ emit connectedChanged ();
}
}
-void BobinkClient::handleEndpointsReceived(
+void
+BobinkClient::handleEndpointsReceived (
const QList<QOpcUaEndpointDescription> &endpoints,
QOpcUa::UaStatusCode statusCode, const QUrl &)
{
- if (statusCode != QOpcUa::Good || endpoints.isEmpty()) {
- emit connectionError(QStringLiteral("Failed to retrieve endpoints"));
- return;
+ if (statusCode != QOpcUa::Good || endpoints.isEmpty ())
+ {
+ emit connectionError (QStringLiteral ("Failed to retrieve endpoints"));
+ return;
}
- QOpcUaEndpointDescription best = endpoints.first();
- for (const auto &ep : endpoints) {
- if (ep.securityLevel() > best.securityLevel())
- best = ep;
+ QOpcUaEndpointDescription best = endpoints.first ();
+ for (const auto &ep : endpoints)
+ {
+ if (ep.securityLevel () > best.securityLevel ())
+ best = ep;
}
- if (m_auth)
- m_client->setAuthenticationInformation(m_auth->toAuthenticationInformation());
-
- m_client->connectToEndpoint(best);
-}
-
-void BobinkClient::handleConnectError(QOpcUaErrorState *errorState)
-{
- if (errorState->connectionStep() ==
- QOpcUaErrorState::ConnectionStep::CertificateValidation) {
- // connectError uses BlockingQueuedConnection — the backend thread is
- // blocked waiting for us to return. The errorState pointer is stack-
- // allocated in the backend, so it is only valid during this call.
- // Spin a local event loop so QML can show a dialog and call
- // acceptCertificate() / rejectCertificate() while we stay in scope.
- m_certAccepted = false;
- emit certificateTrustRequested(
- QStringLiteral("The server certificate is not trusted. Accept?"));
-
- QEventLoop loop;
- m_certLoop = &loop;
- QTimer::singleShot(30000, &loop, &QEventLoop::quit);
- loop.exec();
- m_certLoop = nullptr;
-
- errorState->setIgnoreError(m_certAccepted);
- } else {
- emit connectionError(
- QStringLiteral("Connection error at step %1, code 0x%2")
- .arg(static_cast<int>(errorState->connectionStep()))
- .arg(static_cast<uint>(errorState->errorCode()), 8, 16, QLatin1Char('0')));
+ if (m_auth)
+ m_client->setAuthenticationInformation (
+ m_auth->toAuthenticationInformation ());
+
+ m_client->connectToEndpoint (best);
+}
+
+void
+BobinkClient::handleConnectError (QOpcUaErrorState *errorState)
+{
+ if (errorState->connectionStep ()
+ == QOpcUaErrorState::ConnectionStep::CertificateValidation)
+ {
+ // connectError uses BlockingQueuedConnection — the backend thread is
+ // blocked waiting for us to return. The errorState pointer is stack-
+ // allocated in the backend, so it is only valid during this call.
+ // Spin a local event loop so QML can show a dialog and call
+ // acceptCertificate() / rejectCertificate() while we stay in scope.
+ m_certAccepted = false;
+ emit certificateTrustRequested (
+ QStringLiteral ("The server certificate is not trusted. Accept?"));
+
+ QEventLoop loop;
+ m_certLoop = &loop;
+ QTimer::singleShot (30000, &loop, &QEventLoop::quit);
+ loop.exec ();
+ m_certLoop = nullptr;
+
+ errorState->setIgnoreError (m_certAccepted);
+ }
+ else
+ {
+ emit connectionError (
+ QStringLiteral ("Connection error at step %1, code 0x%2")
+ .arg (static_cast<int> (errorState->connectionStep ()))
+ .arg (static_cast<uint> (errorState->errorCode ()), 8, 16,
+ QLatin1Char ('0')));
}
}
-void BobinkClient::handleFindServersFinished(
+void
+BobinkClient::handleFindServersFinished (
const QList<QOpcUaApplicationDescription> &servers,
QOpcUa::UaStatusCode statusCode, const QUrl &)
{
- if (statusCode != QOpcUa::Good)
- 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);
+ if (statusCode != QOpcUa::Good)
+ 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();
+ emit serversChanged ();
}
diff --git a/src/BobinkClient.h b/src/BobinkClient.h
index 43eda65..c86cfaa 100644
--- a/src/BobinkClient.h
+++ b/src/BobinkClient.h
@@ -35,8 +35,6 @@ class BobinkClient : public QObject
Q_PROPERTY (QString discoveryUrl READ discoveryUrl WRITE setDiscoveryUrl
NOTIFY discoveryUrlChanged)
- Q_PROPERTY (int discoveryInterval READ discoveryInterval WRITE
- setDiscoveryInterval NOTIFY discoveryIntervalChanged)
Q_PROPERTY (bool discovering READ discovering NOTIFY discoveringChanged)
Q_PROPERTY (QVariantList servers READ servers NOTIFY serversChanged)
@@ -71,9 +69,6 @@ public:
QString discoveryUrl () const;
void setDiscoveryUrl (const QString &url);
- int discoveryInterval () const;
- void setDiscoveryInterval (int ms);
-
bool discovering () const;
const QList<QOpcUaApplicationDescription> &discoveredServers () const;
@@ -136,9 +131,9 @@ signals:
*/
void certificateTrustRequested (const QString &certInfo);
void connectionError (const QString &message);
+ void statusMessage (const QString &message);
void discoveryUrlChanged ();
- void discoveryIntervalChanged ();
void discoveringChanged ();
void serversChanged ();
void pkiDirChanged ();
@@ -172,7 +167,7 @@ private:
bool m_certAccepted = false;
QString m_discoveryUrl;
- int m_discoveryInterval = 10000; // ms
+ int m_discoveryInterval = 30000; // ms
QTimer m_discoveryTimer;
bool m_discovering = false;
QList<QOpcUaApplicationDescription> m_discoveredServers;
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 2f10c7a..615e90c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,13 +1,16 @@
-# bobink — QML module wrapping QtOpcUa for declarative use.
-qt_add_qml_module(bobink
- URI Bobink
- VERSION 1.0
- SOURCES
- BobinkAuth.h BobinkAuth.cpp
- BobinkClient.h BobinkClient.cpp
- # BobinkServerDiscovery.h BobinkServerDiscovery.cpp
- # BobinkNode.h BobinkNode.cpp
- OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/qml/Bobink"
-)
+# BobinkQtOpcUa — QML module wrapping QtOpcUa for declarative use.
+qt_add_qml_module(
+ BobinkQtOpcUa
+ URI
+ Bobink
+ VERSION
+ 1.0
+ SOURCES
+ BobinkAuth.h
+ BobinkAuth.cpp
+ BobinkClient.h
+ BobinkClient.cpp
+ OUTPUT_DIRECTORY
+ "${CMAKE_BINARY_DIR}/qml/Bobink")
-target_link_libraries(bobink PRIVATE Qt6::Core Qt6::Quick Qt6::OpcUa)
+target_link_libraries(BobinkQtOpcUa PRIVATE Qt6::Core Qt6::Quick Qt6::OpcUa)