2026-08-04 21:31:40 +05:30
|
|
|
#include "requestworker.h"
|
|
|
|
|
#include <QJsonArray>
|
|
|
|
|
#include <QJsonValue>
|
|
|
|
|
#include <QJsonObject>
|
|
|
|
|
#include <QJsonDocument>
|
2026-08-12 18:22:15 +05:30
|
|
|
#include <QFile>
|
2026-09-23 17:36:00 +05:30
|
|
|
#include <QStandardPaths>
|
2026-08-04 21:31:40 +05:30
|
|
|
|
|
|
|
|
RequestWorker::RequestWorker(QObject *parent)
|
|
|
|
|
: QObject{parent}
|
|
|
|
|
{
|
2026-09-14 15:31:21 +05:30
|
|
|
#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
|
2026-08-04 21:31:40 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
2026-08-12 18:22:15 +05:30
|
|
|
this->alarm_thres = config["alert"].toInt();
|
2026-08-04 21:31:40 +05:30
|
|
|
qDebug() << "In cpp config values are: ";
|
|
|
|
|
qDebug() << "Url: " << url;
|
|
|
|
|
qDebug() << "Interval: " << interval;
|
|
|
|
|
qDebug() << "Max Height: " << max_height;
|
|
|
|
|
qDebug() << "Min Height: " << min_height;
|
2026-08-12 18:22:15 +05:30
|
|
|
qDebug() << "Alert: " << alarm_thres;
|
2026-08-04 21:31:40 +05:30
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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;
|
2026-08-12 14:32:54 +05:30
|
|
|
qDebug() << "Stopping" << timer->isActive();
|
2026-08-04 21:31:40 +05:30
|
|
|
}
|
|
|
|
|
|
2026-09-23 17:36:00 +05:30
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-14 15:31:21 +05:30
|
|
|
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 != "<unknown ssid>") {
|
|
|
|
|
emit connectionText("WiFi connected: " + ssid);
|
|
|
|
|
}
|
|
|
|
|
else if(ssid.length() == 0) {
|
|
|
|
|
emit connectionText("WiFi might not be connected", true);
|
|
|
|
|
}
|
|
|
|
|
else if(ssid != "<unknown ssid>") {
|
|
|
|
|
emit connectionText("Failed to query wifi info", true);
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
emit connectionText("WiFi disconnected", true);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-12 18:22:15 +05:30
|
|
|
void RequestWorker::playAlarm()
|
|
|
|
|
{
|
2026-09-23 17:36:00 +05:30
|
|
|
if (player_started)
|
2026-08-12 18:22:15 +05:30
|
|
|
return;
|
2026-09-23 17:36:00 +05:30
|
|
|
|
|
|
|
|
// 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<void>("setDataSource", "(Ljava/lang/String;)V", jPath.object<jstring>());
|
|
|
|
|
|
|
|
|
|
// Enable looping
|
|
|
|
|
mediaPlayer.callMethod<void>("setLooping", "(Z)V", true);
|
|
|
|
|
|
|
|
|
|
// Prepare and start playback
|
|
|
|
|
mediaPlayer.callMethod<void>("prepare");
|
|
|
|
|
mediaPlayer.callMethod<void>("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
|
2026-08-12 18:22:15 +05:30
|
|
|
if(player) {
|
|
|
|
|
player->deleteLater();
|
|
|
|
|
player = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if(audioOutput) {
|
|
|
|
|
audioOutput->deleteLater();
|
|
|
|
|
audioOutput->deleteLater();
|
|
|
|
|
}
|
|
|
|
|
qDebug() << "Loading player";
|
|
|
|
|
player = new QMediaPlayer(this);
|
|
|
|
|
audioOutput = new QAudioOutput(this);
|
2026-08-12 18:32:45 +05:30
|
|
|
player->setLoops(QMediaPlayer::Loops::Infinite);
|
2026-08-12 18:22:15 +05:30
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-04 21:31:40 +05:30
|
|
|
void RequestWorker::makeRequest()
|
|
|
|
|
{
|
|
|
|
|
if(stop_timer)
|
|
|
|
|
return;
|
|
|
|
|
QNetworkRequest req;
|
2026-09-23 17:36:00 +05:30
|
|
|
req.setRawHeader("Testing", "Something");
|
2026-08-04 21:31:40 +05:30
|
|
|
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<std::chrono::microseconds>(now - prev_time);
|
2026-08-12 14:32:54 +05:30
|
|
|
if(data.size() == 0) {
|
|
|
|
|
prev_time = now;
|
|
|
|
|
timer->start();
|
2026-09-23 17:36:00 +05:30
|
|
|
QString request_response_string = getRequestResponseString(rep);
|
|
|
|
|
emit requestFailed(request_response_string);
|
2026-08-12 14:32:54 +05:30
|
|
|
emit setStatus("Invalid Response; Resending Request");
|
2026-09-23 17:36:00 +05:30
|
|
|
rep->deleteLater();
|
|
|
|
|
rep = nullptr;
|
2026-08-12 14:32:54 +05:30
|
|
|
return;
|
|
|
|
|
}
|
2026-08-04 21:31:40 +05:30
|
|
|
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);
|
2026-08-12 14:32:54 +05:30
|
|
|
float rate_of_fill = ( float )(prev_distance - water_level) /(float) time_in_microseconds;
|
2026-08-04 21:31:40 +05:30
|
|
|
rate_of_fill = rate_of_fill * 1'000'000;
|
2026-08-12 14:58:48 +05:30
|
|
|
int expected_to_fill = (water_level - min_height) / rate_of_fill;
|
2026-09-23 17:36:00 +05:30
|
|
|
int percentage_filled = (float (max_height - water_level) / float (max_height)) * 100;
|
2026-08-12 18:22:15 +05:30
|
|
|
if(enable_alarm) {
|
|
|
|
|
qDebug() << "Alarm enabled";
|
2026-09-23 17:36:00 +05:30
|
|
|
if(water_level <= alarm_thres) {
|
2026-08-12 18:22:15 +05:30
|
|
|
playAlarm();
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-08-04 21:31:40 +05:30
|
|
|
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;
|
2026-08-12 18:22:15 +05:30
|
|
|
qDebug() << "Percentage filled: " << percentage_filled;
|
2026-08-04 21:31:40 +05:30
|
|
|
qDebug() << "Time difference: " << time_in_microseconds;
|
|
|
|
|
prev_distance = water_level;
|
2026-09-23 17:36:00 +05:30
|
|
|
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);
|
2026-08-04 21:31:40 +05:30
|
|
|
if(stop_timer) {
|
|
|
|
|
setStatus("Timer stopped.");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
prev_time = now;
|
|
|
|
|
timer->start();
|
2026-09-23 17:36:00 +05:30
|
|
|
QString status = "Got Reply on: " + timestamp + "; Waiting to Send.";
|
2026-08-04 21:31:40 +05:30
|
|
|
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();
|
|
|
|
|
}
|
2026-08-12 18:22:15 +05:30
|
|
|
|
|
|
|
|
void RequestWorker::startAlarm()
|
|
|
|
|
{
|
|
|
|
|
enable_alarm = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void RequestWorker::stopAlarm()
|
|
|
|
|
{
|
|
|
|
|
enable_alarm = false;
|
|
|
|
|
player_started = false;
|
|
|
|
|
if(player) {
|
|
|
|
|
player->stop();
|
|
|
|
|
player->deleteLater();
|
|
|
|
|
player = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if(audioOutput) {
|
|
|
|
|
audioOutput->deleteLater();
|
|
|
|
|
audioOutput = nullptr;
|
|
|
|
|
}
|
2026-09-23 17:36:00 +05:30
|
|
|
#ifdef Q_OS_ANDROID
|
|
|
|
|
if(m_androidMediaPlayer) {
|
|
|
|
|
delete m_androidMediaPlayer;
|
|
|
|
|
m_androidMediaPlayer = nullptr;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
2026-08-12 18:22:15 +05:30
|
|
|
}
|