diff options
Diffstat (limited to 'src/BobinkClient.cpp')
| -rw-r--r-- | src/BobinkClient.cpp | 325 |
1 files changed, 161 insertions, 164 deletions
diff --git a/src/BobinkClient.cpp b/src/BobinkClient.cpp index 01f9912..6d1c608 100644 --- a/src/BobinkClient.cpp +++ b/src/BobinkClient.cpp @@ -30,12 +30,33 @@ ensurePkiDirs (const QString &base) } } +static QString +securityPolicyUri (BobinkClient::SecurityPolicy policy) +{ + switch (policy) + { + case BobinkClient::Basic256Sha256: + return QStringLiteral ( + "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"); + case BobinkClient::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 {}; +} + +/* ====================================== + * Construction + * ====================================== */ + BobinkClient::BobinkClient (QObject *parent) : QObject (parent), m_provider (new QOpcUaProvider (this)), m_pkiDir (defaultPkiDir ()) { - // Singleton pattern: construct only once - Q_ASSERT(!s_instance); + Q_ASSERT (!s_instance); ensurePkiDirs (m_pkiDir); setupClient (); autoDetectPki (); @@ -87,9 +108,15 @@ BobinkClient::setupClient () } /* ====================================== - * Connection properties + * Connection * ====================================== */ +BobinkClient * +BobinkClient::instance () +{ + return s_instance; +} + bool BobinkClient::connected () const { @@ -132,10 +159,6 @@ BobinkClient::opcuaClient () const return m_client; } -/* ====================================== - * Connection methods - * ====================================== */ - void BobinkClient::connectToServer () { @@ -166,24 +189,6 @@ BobinkClient::connectToServer () m_client->requestEndpoints (url); } -static QString -securityPolicyUri (BobinkClient::SecurityPolicy policy) -{ - switch (policy) - { - case BobinkClient::Basic256Sha256: - return QStringLiteral ( - "http://opcfoundation.org/UA/SecurityPolicy#Basic256Sha256"); - case BobinkClient::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 {}; -} - void BobinkClient::connectDirect (SecurityPolicy policy, SecurityMode mode) { @@ -263,8 +268,77 @@ BobinkClient::rejectCertificate () m_certLoop->quit (); } +void +BobinkClient::handleStateChanged (QOpcUaClient::ClientState state) +{ + bool nowConnected = (state == QOpcUaClient::Connected); + if (m_connected != nowConnected) + { + m_connected = nowConnected; + emit connectedChanged (); + } +} + +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; + } + + 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<quint32> (errorState->errorCode ()), 8, 16, + QLatin1Char ('0'))); + } +} + /* ====================================== - * Discovery properties + * Discovery * ====================================== */ QString @@ -300,6 +374,67 @@ BobinkClient::servers () const return m_serversCache; } +void +BobinkClient::startDiscovery () +{ + if (m_discoveryUrl.isEmpty () || !m_client) + return; + + doDiscovery (); + m_discoveryTimer.start (m_discoveryInterval); + + if (!m_discovering) + { + m_discovering = true; + emit discoveringChanged (); + } +} + +void +BobinkClient::stopDiscovery () +{ + m_discoveryTimer.stop (); + + if (m_discovering) + { + m_discovering = false; + emit discoveringChanged (); + } +} + +void +BobinkClient::doDiscovery () +{ + if (!m_client || m_discoveryUrl.isEmpty ()) + return; + QUrl url (m_discoveryUrl); + if (!url.isValid ()) + return; + m_client->findServers (url); +} + +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); + } + emit serversChanged (); +} + /* ====================================== * PKI * ====================================== */ @@ -434,141 +569,3 @@ BobinkClient::applyPki () QStringLiteral ("PKI applied (no client certificate)")); } } - -/* ====================================== - * Discovery methods - * ====================================== */ - -void -BobinkClient::startDiscovery () -{ - if (m_discoveryUrl.isEmpty () || !m_client) - return; - - doDiscovery (); - m_discoveryTimer.start (m_discoveryInterval); - - if (!m_discovering) - { - m_discovering = true; - emit discoveringChanged (); - } -} - -void -BobinkClient::stopDiscovery () -{ - m_discoveryTimer.stop (); - - if (m_discovering) - { - m_discovering = false; - emit discoveringChanged (); - } -} - -void -BobinkClient::doDiscovery () -{ - 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) -{ - bool nowConnected = (state == QOpcUaClient::Connected); - if (m_connected != nowConnected) - { - m_connected = nowConnected; - emit connectedChanged (); - } -} - -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; - } - - 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<quint32> (errorState->errorCode ()), 8, 16, - QLatin1Char ('0'))); - } -} - -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); - } - emit serversChanged (); -} |
