#include "requestworker.h" #include #include #include #include #include #include RequestWorker::RequestWorker(QObject *parent) : QObject{parent} { #ifdef Q_OS_ANDROID wifi = new WiFiHelper(this); connect(wifi, &WiFiHelper::ssidRetrieved, this, &RequestWorker::gotWiFiSSID); poll_wifi = new QTimer(this); poll_wifi->setInterval(3000); poll_wifi->setSingleShot(false); connect(poll_wifi, &QTimer::timeout, this, &RequestWorker::pollWiFi); wifi->requestPermissionsAndFetch(); poll_wifi->start(); #endif } void RequestWorker::setConfig(QJsonObject config) { this->url = config["url"].toString(); this->interval = config["interval"].toInt(); this->max_height = config["max"].toInt(); this->min_height = config["min"].toInt(); this->alarm_thres = config["alert"].toInt(); qDebug() << "In cpp config values are: "; qDebug() << "Url: " << url; qDebug() << "Interval: " << interval; qDebug() << "Max Height: " << max_height; qDebug() << "Min Height: " << min_height; qDebug() << "Alert: " << alarm_thres; } void RequestWorker::start() { if(manager) { manager->deleteLater(); manager = nullptr; } manager = new QNetworkAccessManager(); if(timer) { timer->deleteLater(); timer = nullptr; } timer = new QTimer(this); timer->setInterval(interval); timer->setSingleShot(true); connect(timer, &QTimer::timeout, this, &RequestWorker::makeRequest); timer->start(); stop_timer = false; setStatus("Waiting to send."); } void RequestWorker::stop() { if(timer->isActive()) timer->stop(); stop_timer = true; qDebug() << "Stopping" << timer->isActive(); } QString RequestWorker::getRequestResponseString(QNetworkReply *rep, QString rep_data) { if(!rep) { return "No Reply"; } QNetworkRequest req = rep->request(); QString errorString = rep->errorString(); QString resp = "GET " + req.url().toString() + "\n"; for(auto &key: req.rawHeaderList()) { QByteArray value = req.rawHeader(key); resp += key + ": " + value + "\n"; } qDebug() << "Raw header list size: " << req.rawHeaderList().size(); QString ret; ret = "Request:\n"; ret += resp; ret += "Response:\n"; if(rep_data == "") ret += errorString; else ret += rep_data; return ret; } void RequestWorker::pollWiFi() { #ifdef Q_OS_ANDROID if(wifi) { wifi->requestPermissionsAndFetch(); } #endif } void RequestWorker::gotWiFiSSID(QString ssid) { if(ssid.length() > 0 && ssid != "Permission Denied" && ssid != "") { emit connectionText("WiFi connected: " + ssid); } else if(ssid.length() == 0) { emit connectionText("WiFi might not be connected", true); } else if(ssid != "") { emit connectionText("Failed to query wifi info", true); } else { emit connectionText("WiFi disconnected", true); } } void RequestWorker::playAlarm() { if (player_started) return; // Step 1: Copy the qrc resource to a local cache file so Android can read it #ifdef Q_OS_ANDROID QString qrcPath = ":/sounds/echo.mp3"; QString cachePath = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + "/echo.mp3"; if (QFile::exists(cachePath)) { QFile::remove(cachePath); } if (QFile::copy(qrcPath, cachePath)) { qDebug() << "Successfully copied resource to cache:" << cachePath; } else { qDebug() << "Failed to copy resource to cache path!"; return; } // Step 2: Use Android JNI to initialize and play via android.media.MediaPlayer QJniEnvironment env; // Create new android.media.MediaPlayer() QJniObject mediaPlayer("android/media/MediaPlayer"); if (!mediaPlayer.isValid()) { qDebug() << "Failed to create native Android MediaPlayer!"; return; } // Set data source using the local cache file path QJniObject jPath = QJniObject::fromString(cachePath); mediaPlayer.callMethod("setDataSource", "(Ljava/lang/String;)V", jPath.object()); // Enable looping mediaPlayer.callMethod("setLooping", "(Z)V", true); // Prepare and start playback mediaPlayer.callMethod("prepare"); mediaPlayer.callMethod("start"); // Store a global reference so it doesn't get garbage collected immediately m_androidMediaPlayer = new QJniObject(mediaPlayer.object()); player_started = true; qDebug() << "Native Android alarm started successfully."; return; #endif if(player) { player->deleteLater(); player = nullptr; } if(audioOutput) { audioOutput->deleteLater(); audioOutput->deleteLater(); } qDebug() << "Loading player"; player = new QMediaPlayer(this); audioOutput = new QAudioOutput(this); player->setLoops(QMediaPlayer::Loops::Infinite); player->setAudioOutput(audioOutput); if (QFile::exists(":/sounds/echo.mp3")) { qDebug() << "Resource found successfully!"; } else { qDebug() << "Resource not found. Check CMakeLists.txt or .pro file!"; } player->setSource(QUrl("qrc:/sounds/echo.mp3")); audioOutput->setVolume(0.5); connect(this, &RequestWorker::playMedia, player, &QMediaPlayer::play); player_started = true; emit playMedia(); } void RequestWorker::makeRequest() { if(stop_timer) return; QNetworkRequest req; req.setUrl(this->url); disconnect(manager, nullptr, nullptr, nullptr); connect(manager, &QNetworkAccessManager::finished, this, &RequestWorker::requestFinished); manager->setTransferTimeout(15000); manager->get(req); setStatus("Request sent; Waiting for Reply."); } void RequestWorker::requestFinished(QNetworkReply *rep) { QByteArray data = rep->readAll(); auto now = std::chrono::high_resolution_clock::now(); auto time_diff = std::chrono::duration_cast(now - prev_time); if(data.size() == 0) { prev_time = now; timer->start(); QString request_response_string = getRequestResponseString(rep); emit requestFailed(request_response_string); emit setStatus("Invalid Response; Resending Request"); rep->deleteLater(); rep = nullptr; return; } unsigned int time_in_microseconds = time_diff.count(); QJsonObject jdata = QJsonDocument::fromJson(data).object(); auto data_water_level = jdata["water_level"].toInt(); float water_level = (float) ((float) (data_water_level) / 10.0); float rate_of_fill = ( float )(prev_distance - water_level) /(float) time_in_microseconds; rate_of_fill = rate_of_fill * 1'000'000; int expected_to_fill = (water_level - min_height) / rate_of_fill; int percentage_filled = (float (max_height - water_level) / float (max_height)) * 100; if(enable_alarm) { qDebug() << "Alarm enabled"; if(water_level <= alarm_thres) { playAlarm(); } } if(reply_number == 0) { rate_of_fill = 0; expected_to_fill = 0; } reply_number = 1; qDebug() << "Got water level: " << water_level; qDebug() << "Got rate of fill: " << rate_of_fill; qDebug() << "Got expected to fill: " << expected_to_fill; qDebug() << "Percentage filled: " << percentage_filled; qDebug() << "Time difference: " << time_in_microseconds; prev_distance = water_level; QString timestamp = QDateTime::currentDateTime().toString("hh:mm:ss"); QString request_response_string = getRequestResponseString(rep, data); request_response_string += "Timestamp:\n" + timestamp; rep->deleteLater(); rep = nullptr; emit networkReply(data_water_level, rate_of_fill, expected_to_fill, request_response_string); if(stop_timer) { setStatus("Timer stopped."); return; } prev_time = now; timer->start(); QString status = "Got Reply on: " + timestamp + "; Waiting to Send."; setStatus(status); } QString RequestWorker::status() const { return m_status; } void RequestWorker::setStatus(const QString &newStatus) { if (m_status == newStatus) return; m_status = newStatus; emit statusChanged(); } void RequestWorker::startAlarm() { enable_alarm = true; } void RequestWorker::stopAlarm() { if (!player_started) return; enable_alarm = false; player_started = false; if(player) { player->stop(); player->deleteLater(); player = nullptr; } if(audioOutput) { audioOutput->deleteLater(); audioOutput = nullptr; } #ifdef Q_OS_ANDROID if (m_androidMediaPlayer && m_androidMediaPlayer->isValid()) { m_androidMediaPlayer->callMethod("stop"); m_androidMediaPlayer->callMethod("release"); delete m_androidMediaPlayer; m_androidMediaPlayer = nullptr; } #endif }