#include "awsconnector.h" #include "loggingcategories.h" #include #include #include #include using namespace awsiotsdk; using namespace awsiotsdk::network; using namespace awsiotsdk::mqtt; QHash AWSConnector::s_requestMap; AWSConnector::AWSConnector(QObject *parent) : QObject(parent) { connect(this, &AWSConnector::connected, this, &AWSConnector::onConnected); } AWSConnector::~AWSConnector() { } void AWSConnector::connect2AWS(const QString &endpoint, const QString &clientId, const QString &caFile, const QString &clientCertFile, const QString &clientPrivKeyFile) { m_networkConnection = std::shared_ptr(new MbedTLSConnection( endpoint.toStdString(), 8883, caFile.toStdString(), clientCertFile.toStdString(), clientPrivKeyFile.toStdString(), std::chrono::milliseconds(30000), std::chrono::milliseconds(30000), std::chrono::milliseconds(30000), true )); m_client = MqttClient::Create(m_networkConnection, std::chrono::milliseconds(30000)); m_client->SetDisconnectCallbackPtr(&onDisconnected, std::shared_ptr(this)); m_client->SetAutoReconnectEnabled(true); m_clientId = clientId; m_connectingFuture = QtConcurrent::run([&]() { ResponseCode rc = m_client->Connect(std::chrono::milliseconds(30000), 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) { qCDebug(dcCloud) << "Connected to AWS."; emit connected(); } else { qCWarning(dcCloud) << "Error connecting to AWS. Response code:" << QString::fromStdString(ResponseHelper::ToString(rc)); m_client.reset(); m_networkConnection.reset(); } }); } DisconnectCallbackContextData::~DisconnectCallbackContextData() {} void AWSConnector::disconnectAWS() { if (isConnected()) { m_client->Disconnect(std::chrono::seconds(2)); } } bool AWSConnector::isConnected() const { return m_connectingFuture.isFinished() && m_networkConnection && m_client && m_client->IsConnected(); } quint16 AWSConnector::publish(const QString &topic, const QVariantMap &message) { if (!isConnected()) { qCWarning(dcCloud()) << "Can't publish to AWS: Not connected."; return -1; } QString fullTopic = QString("%1/%2").arg(m_clientId, topic); QJsonDocument jsonDoc = QJsonDocument::fromVariant(message); uint16_t packetId = 0; ResponseCode res = m_client->PublishAsync(Utf8String::Create(fullTopic.toStdString()), false, false, mqtt::QoS::QOS1, jsonDoc.toJson().toStdString(), &publishCallback, packetId); qCDebug(dcCloud()) << "publish call queued with status:" << QString::fromStdString(ResponseHelper::ToString(res)) << packetId; s_requestMap.insert(packetId, this); return packetId; } void AWSConnector::subscribe(const QStringList &topics) { m_subscribedTopics.append(topics); if (!isConnected()) { qCDebug(dcCloud()) << "Can't subscribe to AWS: Not connected. Subscription will happen upon next connection."; return; } subscribeInternally(topics); } void AWSConnector::onConnected() { if (!m_subscribedTopics.isEmpty()) { subscribeInternally(m_subscribedTopics); } } void AWSConnector::subscribeInternally(const QStringList &topics) { util::Vector> subscription_list; foreach (const QString &topic, topics) { QString finalTopic = QString("%1/%2").arg(m_clientId, topic); qCDebug(dcCloud()) << "topic to subscribe is" << finalTopic << "is valid topic:" << Subscription::IsValidTopicName(finalTopic.toStdString()); auto subscription = mqtt::Subscription::Create(Utf8String::Create(finalTopic.toStdString()), mqtt::QoS::QOS1, &onSubscriptionReceivedCallback, std::shared_ptr(this)); subscription_list.push_back(subscription); } uint16_t packetId; ResponseCode res = m_client->SubscribeAsync(subscription_list, subscribeCallback, packetId); qCDebug(dcCloud()) << "subscribe call queued with status:" << QString::fromStdString(ResponseHelper::ToString(res)) << packetId; s_requestMap.insert(packetId, this); } void AWSConnector::publishCallback(uint16_t actionId, ResponseCode rc) { AWSConnector* obj = s_requestMap.take(actionId); if (!obj) { qCWarning(dcCloud())<< "Received a response callback but don't have an object waiting for it."; return; } switch (rc) { case ResponseCode::SUCCESS: emit obj->responseReceived(actionId, true); qCDebug(dcCloud()) << "Successfully published" << actionId; break; default: qCDebug(dcCloud())<< "Error publishing data to AWS:" << QString::fromStdString(ResponseHelper::ToString(rc)); emit obj->responseReceived(actionId, false); } } void AWSConnector::subscribeCallback(uint16_t actionId, ResponseCode rc) { qCDebug(dcCloud()) << "subscribed to topic" << actionId << QString::fromStdString(ResponseHelper::ToString(rc)); } ResponseCode AWSConnector::onSubscriptionReceivedCallback(util::String topic_name, util::String payload, std::shared_ptr p_app_handler_data) { QJsonParseError error; QJsonDocument jsonDoc = QJsonDocument::fromJson(QByteArray::fromStdString(payload), &error); if (error.error != QJsonParseError::NoError) { qCDebug(dcCloud()) << "Failed to parse JSON from AWS subscription on topic" << QString::fromStdString(topic_name) << ":" << error.errorString() << "\n" << QString::fromStdString(payload); return ResponseCode::JSON_PARSING_ERROR; } AWSConnector *connector = dynamic_cast(p_app_handler_data.get()); QString topic = QString::fromStdString(topic_name); topic.remove(QRegExp("^" + connector->m_clientId + "/")); emit connector->subscriptionReceived(topic, jsonDoc.toVariant().toMap()); return ResponseCode::SUCCESS; } ResponseCode AWSConnector::onDisconnected(util::String mqtt_client_id, std::shared_ptr p_app_handler_data) { Q_UNUSED(p_app_handler_data) qCDebug(dcCloud()) << "disconnected" << QString::fromStdString(mqtt_client_id); return ResponseCode::SUCCESS; }