diff --git a/libguh-core/awsconnector.cpp b/libguh-core/awsconnector.cpp index 833c819e..942bc111 100644 --- a/libguh-core/awsconnector.cpp +++ b/libguh-core/awsconnector.cpp @@ -40,9 +40,6 @@ DisconnectCallbackContextData::~DisconnectCallbackContextData() {} AWSConnector::AWSConnector(QObject *parent) : QObject(parent) { - connect(this, &AWSConnector::connected, this, &AWSConnector::onConnected, Qt::QueuedConnection); - connect(this, &AWSConnector::disconnected, this, &AWSConnector::onDisconnected, Qt::QueuedConnection); - // Enable some AWS logging (does not regard our logging categories) // std::shared_ptr p_log_system = // std::make_shared(awsiotsdk::util::Logging::LogLevel::Info); @@ -61,6 +58,7 @@ AWSConnector::~AWSConnector() void AWSConnector::connect2AWS(const QString &endpoint, const QString &clientId, const QString &clientName, const QString &caFile, const QString &clientCertFile, const QString &clientPrivKeyFile) { + m_shouldReconnect = true; m_currentEndpoint = endpoint; m_caFile = caFile; m_clientCertFile = clientCertFile; @@ -97,7 +95,7 @@ void AWSConnector::doConnect() m_connectingFuture = QtConcurrent::run([&]() { ResponseCode rc = m_client->Connect(std::chrono::milliseconds(3000), true, mqtt::Version::MQTT_3_1_1, std::chrono::seconds(60), Utf8String::Create(m_clientId.toStdString()), nullptr, nullptr, nullptr); if (rc == ResponseCode::MQTT_CONNACK_CONNECTION_ACCEPTED) { - emit connected(); + staticMetaObject.invokeMethod(this, "onConnected", Qt::QueuedConnection); } else { qCWarning(dcAWS) << "Error connecting to AWS. Response code:" << QString::fromStdString(ResponseHelper::ToString(rc)); m_client.reset(); @@ -181,10 +179,12 @@ void AWSConnector::onPairingsRetrieved(const QVariantList &pairings) subscribe(topics); m_setupInProgress = false; + emit connected(); } void AWSConnector::disconnectAWS() { + m_shouldReconnect = false; if (isConnected()) { m_client->Disconnect(std::chrono::seconds(2)); } @@ -229,6 +229,9 @@ quint16 AWSConnector::publish(const QString &topic, const QVariantMap &message) void AWSConnector::onDisconnected() { + qCDebug(dcAWS) << "AWS disconnected."; + emit disconnected(); + bool needReRegistering = false; if (m_setupInProgress) { qCWarning(dcAWS()) << "Setup process interrupted by disconnect."; @@ -244,16 +247,17 @@ void AWSConnector::onDisconnected() qCWarning(dcAWS()) << "Connection dropped 5 times in a row within a minute."; needReRegistering = true; } - return; } if (needReRegistering) { qCDebug(dcAWS) << "Trying to reregister the device in the cloud"; storeRegisteredFlag(false); - doConnect(); } - qCDebug(dcAWS) << "AWS disconnected. (should reconnect on it's own)"; + if (m_shouldReconnect) { + qCDebug(dcAWS()) << "Reconnecting to AWS..."; + doConnect(); + } } void AWSConnector::setName() @@ -409,7 +413,7 @@ ResponseCode AWSConnector::onDisconnectedCallback(util::String mqtt_client_id, s { Q_UNUSED(mqtt_client_id) AWSConnector* connector = dynamic_cast(p_app_handler_data.get())->c; - emit connector->disconnected(); + connector->staticMetaObject.invokeMethod(connector, "onDisconnected", Qt::QueuedConnection); return ResponseCode::SUCCESS; } diff --git a/libguh-core/awsconnector.h b/libguh-core/awsconnector.h index e535ab8c..bf6cfd30 100644 --- a/libguh-core/awsconnector.h +++ b/libguh-core/awsconnector.h @@ -101,6 +101,7 @@ private: QString m_clientName; QFuture m_connectingFuture; bool m_isCleanSession = true; + bool m_shouldReconnect = false; quint8 m_transactionId = 0; QString m_createDeviceId; diff --git a/libguh-core/cloudmanager.cpp b/libguh-core/cloudmanager.cpp index 6cc2d6f2..f124882e 100644 --- a/libguh-core/cloudmanager.cpp +++ b/libguh-core/cloudmanager.cpp @@ -29,6 +29,8 @@ CloudManager::CloudManager(NetworkManager *networkManager, QObject *parent) : QO m_awsConnector = new AWSConnector(this); connect(m_awsConnector, &AWSConnector::devicePaired, this, &CloudManager::onPairingFinished); connect(m_awsConnector, &AWSConnector::webRtcHandshakeMessageReceived, this, &CloudManager::onAWSWebRtcHandshakeMessageReceived); + connect(m_awsConnector, &AWSConnector::connected, this, &CloudManager::awsConnected); + connect(m_awsConnector, &AWSConnector::disconnected, this, &CloudManager::awsDisconnected); m_janusConnector = new JanusConnector(this); connect(m_janusConnector, &JanusConnector::webRtcHandshakeMessageReceived, this, &CloudManager::onJanusWebRtcHandshakeMessageReceived); @@ -113,6 +115,11 @@ void CloudManager::setEnabled(bool enabled) } } +bool CloudManager::connected() const +{ + return m_awsConnector->isConnected(); +} + void CloudManager::pairDevice(const QString &idToken, const QString &userId) { m_awsConnector->pairDevice(idToken, userId); @@ -157,3 +164,13 @@ void CloudManager::onJanusWebRtcHandshakeMessageReceived(const QString &transact { m_awsConnector->sendWebRtcHandshakeMessage(transactionId, data); } + +void CloudManager::awsConnected() +{ + emit connectedChanged(true); +} + +void CloudManager::awsDisconnected() +{ + emit connectedChanged(false); +} diff --git a/libguh-core/cloudmanager.h b/libguh-core/cloudmanager.h index 5244d2c5..04d64484 100644 --- a/libguh-core/cloudmanager.h +++ b/libguh-core/cloudmanager.h @@ -45,12 +45,15 @@ public: bool enabled() const; void setEnabled(bool enabled); + bool connected() const; void pairDevice(const QString &idToken, const QString &userId); bool keepAlive(const QString &sessionId); signals: + void connectedChanged(bool connected); + void pairingReply(QString cognitoUserId, int status, const QString &message); private: @@ -61,6 +64,8 @@ private slots: void onPairingFinished(const QString &cognitoUserId, int errorCode, const QString &message); void onAWSWebRtcHandshakeMessageReceived(const QString &transactionId, const QVariantMap &data); void onJanusWebRtcHandshakeMessageReceived(const QString &transactionId, const QVariantMap &data); + void awsConnected(); + void awsDisconnected(); private: QTimer m_reconnectTimer; diff --git a/libguh-core/jsonrpc/jsonrpcserver.cpp b/libguh-core/jsonrpc/jsonrpcserver.cpp index 83b133c3..7ef1b5b8 100644 --- a/libguh-core/jsonrpc/jsonrpcserver.cpp +++ b/libguh-core/jsonrpc/jsonrpcserver.cpp @@ -141,7 +141,7 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject setReturns("RemoveToken", returns); params.clear(); returns.clear(); - setDescription("SetupRemoteAccess", "Setup the remote connection by providing AWS token information"); + setDescription("SetupRemoteAccess", "Setup the remote connection by providing AWS token information. This requires the cloud to be connected."); params.insert("idToken", JsonTypes::basicTypeToString(JsonTypes::String)); params.insert("userId", JsonTypes::basicTypeToString(JsonTypes::String)); setParams("SetupRemoteAccess", params); @@ -149,6 +149,12 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject returns.insert("message", JsonTypes::basicTypeToString(JsonTypes::String)); setReturns("SetupRemoteAccess", returns); + params.clear(); returns.clear(); + setDescription("IsCloudConnected", "Check whether the cloud is currently connected."); + setParams("IsCloudConnected", params); + returns.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool)); + setReturns("IsCloudConnected", returns); + params.clear(); returns.clear(); setDescription("KeepAlive", "Keep alive a remote connection. The sessionId is the MQTT topic which has been used to establish the session. It will return false if no ongoing session with the given ID can be found."); params.insert("sessionId", JsonTypes::basicTypeToString(JsonTypes::String)); @@ -156,6 +162,12 @@ JsonRPCServer::JsonRPCServer(const QSslConfiguration &sslConfiguration, QObject returns.insert("success", JsonTypes::basicTypeToString(JsonTypes::Bool)); setReturns("KeepAlive", returns); + // Notifications + params.clear(); returns.clear(); + setDescription("CloudConnectedChanged", "Emitted whenever the cloud connection status changes."); + params.insert("connected", JsonTypes::basicTypeToString(JsonTypes::Bool)); + setParams("CloudConnectedChanged", params); + QMetaObject::invokeMethod(this, "setup", Qt::QueuedConnection); } @@ -281,6 +293,15 @@ JsonReply *JsonRPCServer::SetupRemoteAccess(const QVariantMap ¶ms) return reply; } +JsonReply *JsonRPCServer::IsCloudConnected(const QVariantMap ¶ms) +{ + Q_UNUSED(params) + bool connected = GuhCore::instance()->cloudManager()->connected(); + QVariantMap data; + data.insert("connected", connected); + return createReply(data); +} + JsonReply *JsonRPCServer::KeepAlive(const QVariantMap ¶ms) { QString sessionId = params.value("sessionId").toString(); @@ -385,6 +406,7 @@ void JsonRPCServer::setup() registerHandler(new NetworkManagerHandler(this)); connect(GuhCore::instance()->cloudManager(), &CloudManager::pairingReply, this, &JsonRPCServer::pairingFinished); + connect(GuhCore::instance()->cloudManager(), &CloudManager::connectedChanged, this, &JsonRPCServer::onCloudConnectedChanged); } void JsonRPCServer::processData(const QUuid &clientId, const QByteArray &data) @@ -533,6 +555,13 @@ void JsonRPCServer::pairingFinished(QString cognitoUserId, int status, const QSt reply->finished(); } +void JsonRPCServer::onCloudConnectedChanged(bool connected) +{ + QVariantMap params; + params.insert("connected", connected); + emit CloudConnectedChanged(params); +} + void JsonRPCServer::registerHandler(JsonHandler *handler) { m_handlers.insert(handler->name(), handler); diff --git a/libguh-core/jsonrpc/jsonrpcserver.h b/libguh-core/jsonrpc/jsonrpcserver.h index 8ec95ef3..52ae53ad 100644 --- a/libguh-core/jsonrpc/jsonrpcserver.h +++ b/libguh-core/jsonrpc/jsonrpcserver.h @@ -57,14 +57,20 @@ public: Q_INVOKABLE JsonReply *Tokens(const QVariantMap ¶ms) const; Q_INVOKABLE JsonReply *RemoveToken(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *SetupRemoteAccess(const QVariantMap ¶ms); + Q_INVOKABLE JsonReply *IsCloudConnected(const QVariantMap ¶ms); Q_INVOKABLE JsonReply *KeepAlive(const QVariantMap ¶ms); - QHash handlers() const; +signals: + void CloudConnectedChanged(const QVariantMap &map); + // Internal +public: void registerTransportInterface(TransportInterface *interface, bool authenticationRequired); void unregisterTransportInterface(TransportInterface *interface); private: + QHash handlers() const; + void sendResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QVariantMap ¶ms = QVariantMap()); void sendErrorResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error); void sendUnauthorizedResponse(TransportInterface *interface, const QUuid &clientId, int commandId, const QString &error); @@ -83,6 +89,7 @@ private slots: void asyncReplyFinished(); void pairingFinished(QString cognitoUserId, int status, const QString &message); + void onCloudConnectedChanged(bool connected); private: QMap m_interfaces; diff --git a/tests/auto/api.json b/tests/auto/api.json index 0d98a815..07aecdbd 100644 --- a/tests/auto/api.json +++ b/tests/auto/api.json @@ -449,6 +449,14 @@ "types": "Object" } }, + "JSONRPC.IsCloudConnected": { + "description": "Check whether the cloud is currently connected.", + "params": { + }, + "returns": { + "connected": "Bool" + } + }, "JSONRPC.KeepAlive": { "description": "Keep alive a remote connection. The sessionId is the MQTT topic which has been used to establish the session. It will return false if no ongoing session with the given ID can be found.", "params": { @@ -477,7 +485,7 @@ } }, "JSONRPC.SetupRemoteAccess": { - "description": "Setup the remote connection by providing AWS token information", + "description": "Setup the remote connection by providing AWS token information. This requires the cloud to be connected.", "params": { "idToken": "String", "userId": "String" @@ -830,6 +838,12 @@ "event": "$ref:Event" } }, + "JSONRPC.CloudConnectedChanged": { + "description": "Emitted whenever the cloud connection status changes.", + "params": { + "connected": "Bool" + } + }, "Logging.LogDatabaseUpdated": { "description": "Emitted whenever the database was updated. The database will be updated when a log entry was deleted. A log entry will be deleted when the corresponding device or a rule will be removed, or when the oldest entry of the database was deleted to keep to database in the size limits.", "params": { diff --git a/tests/auto/versioning/testversioning.cpp b/tests/auto/versioning/testversioning.cpp index aefcb76c..4361c93a 100644 --- a/tests/auto/versioning/testversioning.cpp +++ b/tests/auto/versioning/testversioning.cpp @@ -102,7 +102,7 @@ void TestVersioning::apiChangeBumpsVersion() p.waitForFinished(); QByteArray apiDiff = p.readAll(); - qDebug() << "API Differences:" << endl << apiDiff; + qDebug() << "API Differences:" << endl << qUtf8Printable(apiDiff); if (oldVersion == newVersionStripped && oldApi != newApi) { QVERIFY2(false, "JSONRPC API has changed but version is still the same. You need to bump the API version.");