From 69086d80934596aca355d172c4465e64f9cd1361 Mon Sep 17 00:00:00 2001 From: TheUnrealDragon Date: Thu, 16 Apr 2026 17:18:02 +0530 Subject: [PATCH 1/2] v0.0.9 --- CMakeLists.txt | 6 +- Main.qml | 48 ++++-- android/AndroidManifest.xml | 4 +- .../WaterLevelService.java | 136 ++++++++++++++-- .../WaterLevelServiceStarter.java | 47 +++++- configinterface.cpp | 18 +-- service.cpp | 151 +++++++++--------- service.h | 2 + udplistener.cpp | 83 +++++++--- udplistener.h | 11 +- 10 files changed, 371 insertions(+), 135 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ddebcab..2b80b0d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,9 +5,9 @@ project(waterLevelSensor VERSION 0.1 LANGUAGES CXX) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_AUTORCC ON) -find_package(Qt6 REQUIRED COMPONENTS Quick Network) +find_package(Qt6 REQUIRED COMPONENTS Quick Network CorePrivate) -qt_standard_project_setup(REQUIRES 6.9) +qt_standard_project_setup(REQUIRES 6.11) qt_add_executable(appwaterLevelSensor main.cpp @@ -41,8 +41,8 @@ set_property(TARGET service APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR target_link_libraries(service PRIVATE Qt::Core - Qt::CorePrivate Qt::Network + Qt::CorePrivate Qt::Quick ) qt_add_qml_module(appwaterLevelSensor diff --git a/Main.qml b/Main.qml index 0f60281..7a705dd 100644 --- a/Main.qml +++ b/Main.qml @@ -634,19 +634,29 @@ Window { } } Timer { + property int count: 0 id: init_timer running: true - repeat: false + repeat: true interval: 1000 onTriggered: { - console.log("Timer triggered saving config, hope everything is loaded by now"); - let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text))); - let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading); - let alert = parseFloat(alert_thres_input.text); - let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading; - console.log("Setting initial configs: ", alert_effective_reading, config.flipBits , config.mode, config.url); - listener.setConfig(alert_effective_reading, config.flipBits , config.mode, config.url); - http_url_input.cursorPosition = 0; + if(count === 0) { + listener.startService(); + count++; + return; + } + else { + console.log("Timer triggered saving config, hope everything is loaded by now"); + let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text))); + let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading); + let alert = parseFloat(alert_thres_input.text); + let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading; + console.log("Setting initial configs: ", alert_effective_reading, config.flipBits , config.mode, config.url); + listener.setConfig(alert_effective_reading, config.flipBits , config.mode, config.url); + http_url_input.cursorPosition = 0; + init_timer.repeat = false; + init_timer.stop(); + } } } @@ -658,9 +668,18 @@ Window { error_text.error_string = err; } - onLastUpdatedChanged: (update) => { + onLastUpdatedChanged: (update, warn) => { console.log("QML Last Updated: ", update); - last_updated_text.last_updated = update; + if(!warn) + { + last_updated_text.last_updated = update; + last_updated_text.color = root.accent_color; + } + else + { + last_updated_text.last_updated = update; + last_updated_text.color = root.danger_color; + } } onDistanceChanged: { @@ -722,9 +741,12 @@ Window { } } - onClosing:(close)=> { + onClosing: (close)=> { if(Qt.platform.os === "android"){ - close.accepted = false + // close.accepted = false + // listener.moveAppToBackground(); + // console.log("Moving to backgound"); + close.accepted = true; } else { close.accepted = true } diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index 7c065f1..ead6548 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -29,7 +29,9 @@ + android:exported="true" + android:process=":water_service" + android:stopWithTask="false"> diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java index 79c4728..2d3709f 100644 --- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java +++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java @@ -32,22 +32,49 @@ public class WaterLevelService extends Service { private boolean bits_flipped = false; private String mode = ""; private String url = ""; + private Thread udpThread; + private Thread cmdThread; + private boolean resetBinds = false; + + public synchronized boolean checkBound() { + return resetBinds; + } + + public synchronized void setBound(boolean value) { + resetBinds = value; + } public class UDPCommandClient implements Runnable { private DatagramSocket socket_cmd_recv; private DatagramSocket socket_cmd_send; + private boolean received_cmd_first = false; + @Override public void run() { Log.d("UDP Service", "Command thread initialising"); try { - socket_cmd_recv = new DatagramSocket(8178, InetAddress.getByName("localhost")); - socket_cmd_send = new DatagramSocket(8179); - + socket_cmd_recv = new DatagramSocket(null); + socket_cmd_send = new DatagramSocket(null); + socket_cmd_recv.setSoTimeout(4000); + socket_cmd_recv.setReuseAddress(true); + socket_cmd_send.setReuseAddress(true); + socket_cmd_recv.bind(new InetSocketAddress(8178)); + socket_cmd_send.bind(new InetSocketAddress(8179)); Log.d("UDP Service", "Created command thread sockets"); while (!Thread.currentThread().isInterrupted()) { + if(socket_cmd_recv.isClosed()) { + socket_cmd_recv.bind(new InetSocketAddress(8178)); + } + if(socket_cmd_send.isClosed()) { + socket_cmd_send.bind(new InetSocketAddress(8179)); + } byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - socket_cmd_recv.receive(packet); + try { + socket_cmd_recv.receive(packet); + } catch(java.net.SocketTimeoutException e) { + continue; + } String str = new String(buffer, StandardCharsets.UTF_8); Log.d("UDP Service ", "Received command packet " + str); if(str.startsWith("", ""); alert_thres_distance = Float.parseFloat(str); + { + if(!checkBound()) + { + setBound(true); + Log.d("UDP Service ", "Set Bound to True"); + } + else { + Log.d("UDP Service ", "Set Bound is False"); + } + + } } else if(str.startsWith(" serviceClass, Context context) { + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (serviceClass.getName().equals(service.service.getClassName())) { + return true; + } + } + return false; + } + + private static int getServicePid(Class serviceClass, Context context) { + ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { + if (serviceClass.getName().equals(service.service.getClassName())) { + return service.pid; + } + } + return 0; + } + public static void startService(Context context) { - Log.d("UDP Service", "Starting service via QtActivity instance"); + // Log.d("UDP Service", "Starting service via QtActivity instance"); - Intent intent = new Intent(context, WaterLevelService.class); + // if(!isMyServiceRunning(WaterLevelService.class, context)) { + // Intent intent = new Intent(context, WaterLevelService.class); + // context.startForegroundService(intent); + // } - context.startForegroundService(intent); + new Handler(Looper.getMainLooper()).post(new Runnable() { + @Override + public void run() { + if (!isMyServiceRunning(WaterLevelService.class, context)) { + Log.d("UDP Service", "Starting service safely on Main Looper"); + Intent intent = new Intent(context, WaterLevelService.class); + context.startForegroundService(intent); + } + else { + Log.d("UDP Service", "Service already running"); + } + Log.d("UDP Service", "Background service running with pid: " + getServicePid(WaterLevelService.class, context)); + } + }); } } diff --git a/configinterface.cpp b/configinterface.cpp index ab97ecb..73ae20a 100644 --- a/configinterface.cpp +++ b/configinterface.cpp @@ -23,9 +23,9 @@ ConfigInterface::ConfigInterface(QObject *parent) QJsonObject top = QJsonDocument::fromJson(data).object(); QString max, min, alert, mode, url; bool flipbits; - max = top["max_threshold"].toString("100"); - min = top["min_threshold"].toString("20"); - alert = top["alert_threshold"].toString("60"); + max = top["max_threshold"].toString("110"); + min = top["min_threshold"].toString("10"); + alert = top["alert_threshold"].toString("30"); flipbits = top["flip_bits"].toBool(true); mode = top["mode"].toString("http"); url = top["url"].toString("https://unrealdragon.in/server/waterlevel"); @@ -39,9 +39,9 @@ ConfigInterface::ConfigInterface(QObject *parent) else { qDebug() << "File does not exist creating one..."; QJsonObject top; - top["max_threshold"] = "125"; - top["min_threshold"] = "25"; - top["alert_threshold"] = "100"; + top["max_threshold"] = "110"; + top["min_threshold"] = "10"; + top["alert_threshold"] = "30"; top["flip_bits"] = true; top["mode"] = "http"; top["url"] = "https://unrealdragon.in/server/waterlevel"; @@ -54,9 +54,9 @@ ConfigInterface::ConfigInterface(QObject *parent) config.write(data); config.flush(); config.close(); - setMax("125"); - setMin("25"); - setAlert("100"); + setMax("110"); + setMin("10"); + setAlert("30"); setFlipBits(true); setMode("http"); setUrl("https://unrealdragon.in/server/waterlevel"); diff --git a/service.cpp b/service.cpp index 6b819b0..f48e455 100644 --- a/service.cpp +++ b/service.cpp @@ -11,7 +11,6 @@ Q_DECLARE_JNI_CLASS(ComponentName, "android/content/ComponentName") Q_DECLARE_JNI_CLASS(WaterLevelService, "org/qtproject/example/appwaterLevelSensor/WaterLevelService") - static volatile float level = -1; static void handleWaterLevelUpdate(JNIEnv *env, jobject /*obj*/, jbyteArray data) { @@ -29,87 +28,95 @@ static void handleWaterLevelUpdate(JNIEnv *env, jobject /*obj*/, jbyteArray data env->ReleaseByteArrayElements(data, bytes, JNI_ABORT); } +extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) { + JNIEnv* env; + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) { + return JNI_ERR; + } + + // Register your native method ONCE when the library loads + jclass javaClass = env->FindClass("org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver"); + if (javaClass) { + static JNINativeMethod methods[] = { + {"updateWaterLevel", "([B)V", (void *)handleWaterLevelUpdate} + }; + env->RegisterNatives(javaClass, methods, 1); + } + + return JNI_VERSION_1_6; +} + + service::service(QObject *parent) : QObject{parent} { if(QSysInfo::productType() == "android") { qDebug() << "Running android service"; + // QJniEnvironment env; + // // if (env->ExceptionCheck()) env->ExceptionClear(); + // // static bool nativesRegistered = false; - using namespace QtJniTypes; - using namespace QNativeInterface; + // // if(!nativesRegistered) { + // // // jclass javaClass = env.findClass("org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver"); + // // // if (javaClass) { + // // // JNINativeMethod methods[] = {{"updateWaterLevel", "([B)V", (void *)handleWaterLevelUpdate}}; + // // // if (env->RegisterNatives(javaClass, methods, 1) < 0) { + // // // qDebug() << "RegisterNatives failed!"; + // // // } else { + // // // nativesRegistered = true; + // // // } + // // // } else { + // // // qDebug() << "Could not find WaterLevelReceiver class"; + // // // } + // // // if (env->ExceptionCheck()) env->ExceptionClear(); + // // } + // using namespace QtJniTypes; + // using namespace QNativeInterface; - JNINativeMethod methods[] = { - {"updateWaterLevel", "([B)V", (void *)handleWaterLevelUpdate} - }; - // auto *androidApp = qGuiApp->nativeInterface(); - // Context context = androidApp->context(); - QJniObject context = QNativeInterface::QAndroidApplication::context(); - - QJniEnvironment env; - auto serviceClass = env.findClass(Traits::className()); - if (!serviceClass) { - qDebug() << "FAILED to find WaterLevelService class!"; - env->ExceptionDescribe(); // VERY important - env->ExceptionClear(); - return; - } - - jclass javaClass = env.findClass("org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver"); - env->RegisterNatives(javaClass, methods, 1); - - - QJniObject::callStaticMethod( - "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter", - "startService", - "(Landroid/content/Context;)V", - context.object()); // pass the QtActivity instance ); - - // QJniObject activity = QNativeInterface::QAndroidApplication::context(); - // activity.callMethod("startWaterLevelService", "()V"); - // Intent serviceIntent(activity, serviceClass); - // activity.callMethod("startWaterLevelService"); - - // activity.callMethod<() - // QJniObject::callStaticMethod( - // "org/qtproject/example/appwaterLevelSensor/WaterLevelService", - // "startQtAndroidService", - // "(Landroid/content/Context;)V", - // activity.object() - // ); - - // activity.callMethod("startWaterLevelService", "()V"); // Intent serviceIntent(context, serviceClass); - // context.callMethod("startService", serviceIntent); - - // context.callMethod( - // "startForegroundService", - // serviceIntent - // ); - // QJniObject className = QJniObject::fromString( - // "org.qtproject.example.appwaterLevelSensor.WaterLevelService" - // ); - - // QJniObject serviceClassObj = QJniObject::callStaticObjectMethod( - // "java/lang/Class", - // "forName", - // "(Ljava/lang/String;)Ljava/lang/Class;", - // className.object() - // ); - - // QJniObject intent( - // "android/content/Intent", - // "(Landroid/content/Context;Ljava/lang/Class;)V", - // context.object(), - // serviceClassObj.object() - // ); - - // context.callObjectMethod( - // "startForegroundService", - // "(Landroid/content/Intent;)Landroid/content/ComponentName;", - // intent.object() - // ); // WaterLevelService::callStaticMethod("startQtAndroidService", context); + // // Context context = androidApp->context(); + // QJniObject context = QNativeInterface::QAndroidApplication::context(); + // auto serviceClass = env.findClass(Traits::className()); + // if (!serviceClass) { + // qDebug() << "FAILED to find WaterLevelService class!"; + // env->ExceptionDescribe(); // VERY important + // env->ExceptionClear(); + // return; + // } + // if (context.isValid()) { + // QJniObject::callStaticMethod< + // void>( + // "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter", + // "startService", + // "(Landroid/content/Context;)V", + // context.object()); + // } } } + +void service::moveAppToBackground() +{ + QJniObject activity = QNativeInterface::QAndroidApplication::context(); + if (activity.isValid()) { + qDebug() << "Moved app to background"; + activity.callMethod("moveTaskToBack", "(Z)Z", true); + } + qDebug() << "Closing"; +} + +void service::begin() { + + + // Only call this once the UI is visible + QJniObject context = QNativeInterface::QAndroidApplication::context(); + QJniObject::callStaticMethod( + "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter", + "startService", + "(Landroid/content/Context;)V", + context.object() + ); +} + diff --git a/service.h b/service.h index 5b64765..1ba18a1 100644 --- a/service.h +++ b/service.h @@ -8,6 +8,8 @@ class service : public QObject Q_OBJECT public: explicit service(QObject *parent = nullptr); + void moveAppToBackground(); + void begin(); signals: }; diff --git a/udplistener.cpp b/udplistener.cpp index 52aaa8d..e44a9eb 100644 --- a/udplistener.cpp +++ b/udplistener.cpp @@ -1,15 +1,12 @@ #include "udplistener.h" #include -#ifdef Q_OS_ANDROID -#include "service.h" -#endif UDPListener::UDPListener(QObject *parent) : QObject{parent} { - worker = new Worker(this); - thread = new QThread(this); + worker = new Worker(); + thread = new QThread(); worker->moveToThread(thread); connect(worker, &Worker::lastUpdateddatagramReceived, this, &UDPListener::lastUpdateddatagramReceived); connect(worker, &Worker::datagramReceived, this, &UDPListener::datagramRecieved); @@ -23,11 +20,6 @@ UDPListener::UDPListener(QObject *parent) timer->setSingleShot(false); connect(timer, &QTimer::timeout, this, &UDPListener::connectionTimerTimedOut); connect(qApp, &QCoreApplication::aboutToQuit, this, &UDPListener::destroy); -#ifdef Q_OS_ANDROID - qDebug() << "Running on Android"; - service service; - qDebug() << "Ran service"; -#endif } double UDPListener::distance() const @@ -41,11 +33,29 @@ void UDPListener::setDistance(double newDistance) emit distanceChanged(); } +void UDPListener::moveAppToBackground() +{ +#ifdef Q_OS_ANDROID + service->moveAppToBackground(); +#endif +} + void UDPListener::lastUpdateddatagramReceived(QByteArray data) { QString s_data = QString::fromUtf8(data); qDebug() << "Main Class last updated on: " << s_data; - emit lastUpdatedChanged(s_data); + QDateTime time = QDateTime::fromString(s_data, "dd/MM/yy hh:mm:ss"); + QDateTime thres_time = QDateTime::currentDateTime().addSecs(-1800); + if(time.isValid() && time < thres_time) + { + emit lastUpdatedChanged(s_data, false); + } + else if(time > thres_time && time.isValid()) { + emit lastUpdatedChanged(s_data, true); + } + else if(!time.isValid()) { + emit lastUpdatedChanged("Invalid Date", true); + } } void UDPListener::continueDestruction() @@ -114,6 +124,9 @@ void UDPListener::setConfig(float distance, bool flip_bits, QString mode, QStrin emit changeWorkerMode(mode, url); } + else { + showError("Command Socket Bind Failed"); + } } averages.clear(); } @@ -129,6 +142,12 @@ void UDPListener::socketError(QString err) void UDPListener::startService() { +#ifdef Q_OS_ANDROID + qDebug() << "Running on Android"; + service = new class service(); + qDebug() << "Ran service"; + service->begin(); +#endif } @@ -163,25 +182,31 @@ void UDPListener::datagramRecieved(QByteArray data1, QByteArray data2) void UDPListener::destroy() { qDebug() << "Destroying UDP Listener"; + +#ifdef Q_OS_ANDROID + delete service; +#endif + disconnect(timer, nullptr, nullptr, nullptr); timer->stop(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); connect(this, &UDPListener::stopWorker, worker, &Worker::stop); connect(worker, &Worker::stopped, this, &UDPListener::continueDestruction); emit stopWorker(); while(!destroyed) { - QCoreApplication::processEvents(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); } thread->exit(0); - QCoreApplication::processEvents(); - QCoreApplication::processEvents(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); worker->deleteLater(); - QCoreApplication::processEvents(); - QCoreApplication::processEvents(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); + while(thread->isRunning()) { + qDebug() << "The thread is still running."; + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); + } thread->deleteLater(); - QCoreApplication::processEvents(); - QCoreApplication::processEvents(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); timer->deleteLater(); - QCoreApplication::processEvents(); - QCoreApplication::processEvents(); + QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); qDebug() << "Deinitialized UDP Listener"; } @@ -234,6 +259,7 @@ void Worker::gotHttpReply(QNetworkReply *rep) void Worker::httpTimerTimeout() { + qDebug() << "Worker httpTimer Timeout"; QNetworkRequest req; req.setUrl(QUrl("https://unrealdragon.in/server/waterlevel")); disconnect(manager, nullptr, nullptr, nullptr); @@ -243,8 +269,16 @@ void Worker::httpTimerTimeout() void Worker::stop() { - disconnect(socket, nullptr, nullptr, nullptr); - delete socket; + if(socket_2) { + disconnect(socket_2, nullptr, nullptr, nullptr); + socket_2->close(); + delete socket_2; + } + if(socket) { + disconnect(socket, nullptr, nullptr, nullptr); + socket->close(); + delete socket; + } if(httpTimer) { disconnect(httpTimer, nullptr, nullptr, nullptr); delete httpTimer; @@ -265,8 +299,8 @@ void Worker::run() manager = new QNetworkAccessManager(this); #ifdef Q_OS_ANDROID // If it is android use it as a command socket; - bool bind = socket->bind(QHostAddress::Any, 8180); - bool bind2 = socket_2->bind(QHostAddress::Any, 8185); + bool bind = socket->bind(QHostAddress::Any, 8180, QUdpSocket::ReuseAddressHint); + bool bind2 = socket_2->bind(QHostAddress::Any, 8185, QUdpSocket::ReuseAddressHint); #else // Else use it as a receiver; bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176); @@ -277,6 +311,7 @@ void Worker::run() qDebug() << "UI Worker socket bind failed" << bind << socket->errorString(); emit socketError(socket->errorString()); emit socketError(socket_2->errorString()); + qApp->exit(-1); } connect(httpTimer, &QTimer::timeout, this, &Worker::httpTimerTimeout); connect(socket, &QUdpSocket::readyRead, this, &Worker::socketDataReceived); diff --git a/udplistener.h b/udplistener.h index 0a950e5..8c123c8 100644 --- a/udplistener.h +++ b/udplistener.h @@ -16,6 +16,11 @@ #include #include "moving_container.h" +#ifdef Q_OS_ANDROID +#include "service.h" +#endif + + class Worker: public QObject { Q_OBJECT QUdpSocket *socket = nullptr; @@ -48,12 +53,15 @@ class UDPListener : public QObject QML_ELEMENT Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged FINAL) bool destroyed = false; +#ifdef Q_OS_ANDROID + service *service = nullptr; +#endif public: explicit UDPListener(QObject *parent = nullptr); double distance() const; void setDistance(double newDistance); signals: - void lastUpdatedChanged(QString last_updated); + void lastUpdatedChanged(QString last_updated, bool warn); void stopWorker(); void showError(QString err); void distanceChanged(); @@ -63,6 +71,7 @@ signals: void distanceReceived(double dist, QString timestamp); void changeWorkerMode(QString mode, QString url); public slots: + void moveAppToBackground(); void lastUpdateddatagramReceived(QByteArray data); void continueDestruction(); void resetAverages(); From e6d5589cadf003b8a0809a6f2589699c23839f7d Mon Sep 17 00:00:00 2001 From: TheUnrealDragon Date: Fri, 17 Apr 2026 15:43:25 +0530 Subject: [PATCH 2/2] v0.1.0 --- Main.qml | 45 ++++- README.md | 11 +- .../WaterLevelService.java | 182 ++++++++++++----- .../WaterLevelServiceStarter.java | 68 ++++++- udplistener.cpp | 190 +++++++++++++----- udplistener.h | 18 ++ 6 files changed, 410 insertions(+), 104 deletions(-) diff --git a/Main.qml b/Main.qml index 7a705dd..e7844b9 100644 --- a/Main.qml +++ b/Main.qml @@ -19,11 +19,47 @@ Window { property int font_normal_text_size: 16 property double distance: 0 property int distance_count: 0 + property int distance_count_thres: 3 property string mode: "http" // visibility: Window.FullScreen Component.onCompleted: { - if(Qt.platform.os !== "android") { + loading_popup.open(); + } + + + Popup { + id: loading_popup + background: Rectangle { + color: "#B0121212" + radius: 5 + border.color: root.accent_color + border.width: 1 } + height: parent.height / 10 + width: parent.width / 1.1 + anchors.centerIn: Overlay.overlay + closePolicy: Popup.NoAutoClose + BusyIndicator { + id: loading_indicator + running: true + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + } + Text { + id: loading_text + text: "Loading..." + anchors { + leftMargin: 20 + left: loading_indicator.right + verticalCenter: parent.verticalCenter + } + verticalAlignment: Text.AlignVCenter + color: root.accent_color + font.pointSize: root.font_enlarged_text_size + } + } Rectangle { @@ -687,7 +723,10 @@ Window { root.distance = listener.distance; error_text.error_string = ""; console.log("Distance count: ", root.distance_count); - if(root.distance_count >= 5) { + if(root.distance_count >= root.distance_count_thres) { + if(loading_popup.opened) { + loading_popup.close(); + } status_label.text = "Recieving values"; let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text))); let effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading); @@ -704,7 +743,7 @@ Window { water_level.color = root.water_color; } if(root.distance_count > 1000) { - root.distance_count = 5; + root.distance_count = root.distance_count_thres; } console.log("Current reading is: " + current_reading) console.log("Normalized height is: " + normalized_height) diff --git a/README.md b/README.md index 96a9151..7198d51 100644 --- a/README.md +++ b/README.md @@ -5,4 +5,13 @@ Builds for android and desktop.
On Android Creates a service from service.cpp and android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java.
Changes in AndroidManifest.xml are important.
On Desktop just waits for values from a UDP Broadcast on local network with port 8176 and shows those values.
-On Android the service does that, and creates an alert notification if the value in broadcast is less than alert threshold.
\ No newline at end of file +On Android the service does that, and creates an alert notification if the value in broadcast is less than alert threshold.
+ + +
+Create a new socket in Worker that will instantly start listening.
+In the service launch the threads and immediately send client hello to the worker socket.
+On client hello send command data to the android service.
+On quit send server bye to android service.
+On server bye deinit the sockets that send data to cpp server.
+On next launch, the command and client threads can be reinitialized from the StartService java class.
\ No newline at end of file diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java index 2d3709f..cc765d0 100644 --- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java +++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java @@ -25,25 +25,87 @@ import org.json.JSONException; import org.json.JSONObject; import org.json.JSONArray; import android.widget.Toast; +import android.os.IBinder; +import android.os.Handler; +import android.os.Message; +import android.os.Messenger; public class WaterLevelService extends Service { private static float alert_thres_distance = 1000000; - private boolean bits_flipped = false; - private String mode = ""; - private String url = ""; - private Thread udpThread; - private Thread cmdThread; - private boolean resetBinds = false; + private static boolean bits_flipped = false; + private static String mode = ""; + private static String url = ""; + private static Thread udpThread; + private static Thread cmdThread; + private static boolean resetBinds = false; + private final IBinder binder = new LocalBinder(); + final Messenger mMessenger = new Messenger(new IncomingHandler()); + public static final int MSG_RESTART_THREADS = 1; - public synchronized boolean checkBound() { + + class IncomingHandler extends Handler { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_RESTART_THREADS: + Log.d("UDP Service MainThread", "Received IPC request to restart threads"); + restartThreads(); + break; + default: + super.handleMessage(msg); + } + } + } + + + public synchronized void restartThreads() { + Log.d("UDP Service MainThread", "In synchronized thread stopper."); + udpThread.interrupt(); + cmdThread.interrupt(); + try { + udpThread.join(); + cmdThread.join(); + } catch (InterruptedException e) { + Log.d("UDP Service MainThread", "Thread joining was interrupted because of: " + e); + } + + new Thread(new + Runnable() { + @Override + public void run() { + try { + // Move your socket and thread starting logic here + if (udpThread == null || !udpThread.isAlive()) { + udpThread = new Thread(new UDPClient()); + udpThread.start(); + } + if (cmdThread == null || !cmdThread.isAlive()) { + cmdThread = new Thread(new UDPCommandClient()); + cmdThread.start(); + } + } catch (Exception e) { + Log.e("UDP Service MainThread", "Restarting the threads", e); + } + } + }).start(); + Log.d("UDP Service MainThread", "Actually restarting the thread"); + } + + public static synchronized boolean checkBound() { return resetBinds; } - public synchronized void setBound(boolean value) { + public static synchronized void setBound(boolean value) { resetBinds = value; } + public class LocalBinder extends android.os.Binder { + WaterLevelService getService() { + return WaterLevelService.this; + } + } + public class UDPCommandClient implements Runnable { private DatagramSocket socket_cmd_recv; private DatagramSocket socket_cmd_send; @@ -51,7 +113,7 @@ public class WaterLevelService extends Service { @Override public void run() { - Log.d("UDP Service", "Command thread initialising"); + Log.d("UDP Service CommandThread", "Command thread initialising"); try { socket_cmd_recv = new DatagramSocket(null); socket_cmd_send = new DatagramSocket(null); @@ -59,15 +121,30 @@ public class WaterLevelService extends Service { socket_cmd_recv.setReuseAddress(true); socket_cmd_send.setReuseAddress(true); socket_cmd_recv.bind(new InetSocketAddress(8178)); - socket_cmd_send.bind(new InetSocketAddress(8179)); - Log.d("UDP Service", "Created command thread sockets"); + socket_cmd_send.bind(new InetSocketAddress(8187)); + boolean not_send_set = true; + Log.d("UDP Service CommandThread", "Created command thread sockets"); while (!Thread.currentThread().isInterrupted()) { if(socket_cmd_recv.isClosed()) { socket_cmd_recv.bind(new InetSocketAddress(8178)); } if(socket_cmd_send.isClosed()) { - socket_cmd_send.bind(new InetSocketAddress(8179)); + socket_cmd_send.bind(new InetSocketAddress(8187)); } + + if(not_send_set) { + String s_data = new String(""); + byte[] buff_s = s_data.getBytes(StandardCharsets.UTF_8); + DatagramPacket packet = new DatagramPacket(buff_s, buff_s.length, InetAddress.getByName("127.0.0.1"), 8186); + try { + socket_cmd_send.connect(InetAddress.getByName("127.0.0.1"), 8186); + socket_cmd_send.send(packet); + } catch(Exception e) { + Log.d("UDP Service CommandThread", "Failed to send data to client due to: " + e); + } + Log.d("UDP Service CommandThread", "Sent set message to client"); + } + byte[] buffer = new byte[1024]; DatagramPacket packet = new DatagramPacket(buffer, buffer.length); try { @@ -76,7 +153,8 @@ public class WaterLevelService extends Service { continue; } String str = new String(buffer, StandardCharsets.UTF_8); - Log.d("UDP Service ", "Received command packet " + str); + Log.d("UDP Service CommandThread", "Received command packet " + str); + not_send_set = false; if(str.startsWith("", ""); - Log.d("UDP Service ", "Flipping " + str); + Log.d("UDP Service CommandThread", "Flipping " + str); if(str.toLowerCase() == "false") { bits_flipped = false; } @@ -110,21 +188,21 @@ public class WaterLevelService extends Service { str = str.replace("", ""); - Log.d("UDP Service ", "Mode " + str); + Log.d("UDP Service CommandThread", "Mode " + str); mode = str.toLowerCase(); } else if(str.startsWith("", ""); - Log.d("UDP Service ", "Url " + str); + Log.d("UDP Service CommandThread", "Url " + str); url = str.toLowerCase(); } } } catch (IOException e) { // showToast("IOException: " + e); - Log.e("UDP Service", "IOException Error" + e); + Log.e("UDP Service CommandThread", "IOException Error" + e); } } @@ -134,11 +212,12 @@ public class WaterLevelService extends Service { private DatagramSocket socket_recv; private DatagramSocket socket_send; private Instant prevAlertTimestamp; + private InetAddress address; private boolean started = false; @Override public void run() { - Log.d("UDP Service", "Client Thread initialising"); + Log.d("UDP Service ClientThread", "Client Thread initialising"); android.net.wifi.WifiManager wifi = (android.net.wifi.WifiManager) getSystemService(Context.WIFI_SERVICE); android.net.wifi.WifiManager.MulticastLock multicastLock = wifi.createMulticastLock("water_level_lock"); android.net.wifi.WifiManager.WifiLock wifiLock = wifi.createWifiLock(android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF, "WaterLevel:WifiLock"); @@ -167,16 +246,19 @@ public class WaterLevelService extends Service { socket_send.setReuseAddress(true); socket_send.bind(new InetSocketAddress(8177)); socket_recv.bind(new InetSocketAddress(8176)); - Log.d("UDP Service", "Created Sockets"); + Log.d("UDP Service ClientThread", "Created Sockets"); int count = 0; while (!Thread.currentThread().isInterrupted()) { count++; if(count == 3) { - Log.i("UDP Service", "Thread is running with mode: " + mode); + Log.i("UDP Service ClientThread", "Thread is running with mode: " + mode); count = 0; } if(checkBound()) { - Log.i("UDP Service", "Reconnecting sockets"); + Log.i("UDP Service ClientThread", "Reconnecting sockets"); + this.address = InetAddress.getByName("127.0.0.1"); + socket_recv.disconnect(); + socket_send.disconnect(); socket_recv.close(); socket_send.close(); socket_recv = null; @@ -190,7 +272,7 @@ public class WaterLevelService extends Service { socket_send.bind(new InetSocketAddress(8177)); socket_recv.bind(new InetSocketAddress(8176)); setBound(false); - Log.i("UDP Service", "Reconnected sockets"); + Log.i("UDP Service ClientThread", "Reconnected sockets"); } // if(checkBound()) { // Log.i("UDP Service", "Reset socket binds: " + mode); @@ -238,9 +320,10 @@ public class WaterLevelService extends Service { try { Thread.sleep(2000); } catch (InterruptedException e) { - continue; + Log.d("UDP Service ClientThread", "Thread exiting due to interrupt."); + return; } - Log.d("UDP Service", "Http String Url: " + url); + Log.d("UDP Service ClientThread", "Http String Url: " + url); URL t_url = new URL(url); HttpURLConnection con = (HttpURLConnection) t_url.openConnection(); con.setRequestMethod("GET"); @@ -248,7 +331,7 @@ public class WaterLevelService extends Service { con.setReadTimeout(5000); con.setInstanceFollowRedirects(true); int status = con.getResponseCode(); - Log.d("UDP Service", "Got status from http: " + status); + Log.d("UDP Service ClientThread", "Got status from http: " + status); if(status == 200) { BufferedReader streamReader = new BufferedReader(new InputStreamReader(con.getInputStream())); String data = streamReader.readLine(); @@ -258,10 +341,10 @@ public class WaterLevelService extends Service { float f = (float ) arr.getDouble(0); updated = obj.getString("updated_on"); buffer = ByteBuffer.allocate(4).putFloat(f).array(); - Log.d("UDP Service", "Got value from http: " + f); - Log.d("UDP Service", "Last updated on: " + updated); + Log.d("UDP Service ClientThread", "Got value from http: " + f); + Log.d("UDP Service ClientThread", "Last updated on: " + updated); } catch (JSONException e) { - Log.e("UDP Service", "Invalid Json"); + Log.e("UDP Service ClientThread", "Invalid Json"); // showToast("Invalid Json" + e); continue; } @@ -273,12 +356,13 @@ public class WaterLevelService extends Service { } } else { - Log.d("UDP Service", "Invalid mode: " + mode); + Log.d("UDP Service ClientThread", "Invalid mode: " + mode); // showToast("Invalid mode: " + mode); try { Thread.sleep(3000); } catch (InterruptedException e) { - continue; + Log.d("UDP Service ClientThread", "Thread exiting due to interrupt."); + return; } continue; } @@ -289,8 +373,8 @@ public class WaterLevelService extends Service { else { f = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN).getFloat(); } - Log.d("UDP Service ", "Received packet: " + f); - Log.d("UDP Service ", "Threshold is: " + alert_thres_distance); + Log.d("UDP Service ClientThread", "Received packet: " + f); + Log.d("UDP Service ClientThread", "Threshold is: " + alert_thres_distance); boolean duration_passed = false; if(!started) { duration_passed = true; @@ -311,7 +395,7 @@ public class WaterLevelService extends Service { .setContentText("Water Level is below alert threshold") .setPriority(Notification.PRIORITY_LOW); manager.notify(2, builder.build()); - Log.d("UDP Service", "Notification sent"); + Log.d("UDP Service ClientThread", "Notification sent"); if(!started) { started = true; } @@ -319,20 +403,20 @@ public class WaterLevelService extends Service { if(socket_send.isClosed()) { socket_send.bind(new InetSocketAddress(8177)); } - DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8180); + DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("127.0.0.1"), 8180); socket_send.send(send_packet); - Log.d("UDP Service ", "Sent packet"); + Log.d("UDP Service ClientThread", "Sent packet"); if(mode.equals("http")) { byte[] buff2 = updated.getBytes(StandardCharsets.UTF_8); - DatagramPacket send_packet_2 = new DatagramPacket(buff2, buff2.length, InetAddress.getByName("localhost"), 8185); + DatagramPacket send_packet_2 = new DatagramPacket(buff2, buff2.length, InetAddress.getByName("127.0.0.1"), 8185); socket_send.send(send_packet_2); - Log.d("UDP Service ", "Sent last updated packet"); + Log.d("UDP Service ClientThread", "Sent last updated packet"); } } } catch (IOException e) { // showToast("IOException: " + e); - Log.e("UDP Service", "IOException Error" + e); + Log.e("UDP Service ClientThread", "IOException Error" + e); } finally { if (wifiLock.isHeld()) wifiLock.release(); @@ -347,7 +431,7 @@ public class WaterLevelService extends Service { } public void startWaterLevelService() { - Log.d("UDP Service", "Starting the service from Activity"); + Log.d("UDP Service MainThread", "Starting the service from Activity"); Intent intent = new Intent(this, WaterLevelService.class); @@ -355,25 +439,25 @@ public class WaterLevelService extends Service { } public WaterLevelService() { super(); - Log.i("UDP Service", "Class is created"); + Log.i("UDP Service MainThread", "Class is created"); } @Override - public android.os.IBinder onBind(android.content.Intent intent) { return null; } + public android.os.IBinder onBind(android.content.Intent intent) { return mMessenger.getBinder(); } @Override public void onCreate() { super.onCreate(); Notification notification = createNotification(); startForeground(1, notification); - Log.i("UDP Service", "Creating Service"); + Log.i("UDP Service MainThread", "Creating Service"); } @Override public void onDestroy() { super.onDestroy(); - Log.i("UDP Service", "Destroying Service"); + Log.i("UDP Service MainThread", "Destroying Service"); } @Override @@ -383,7 +467,7 @@ public class WaterLevelService extends Service { if (intent == null) { - Log.w("UDP Service", "Service restarted with null intent. Stopping to avoid crash."); + Log.w("UDP Service MainThread", "Service restarted with null intent. Stopping to avoid crash."); stopSelf(); return START_NOT_STICKY; } @@ -402,12 +486,12 @@ public class WaterLevelService extends Service { cmdThread.start(); } } catch (Exception e) { - Log.e("UDP Service", "Error starting threads", e); + Log.e("UDP Service MainThread", "Error starting threads", e); } } }).start(); - Log.d("UDP Service", "Actually starting the thread"); + Log.d("UDP Service MainThread", "Actually starting the thread"); int ret = super.onStartCommand(intent, flags, startId); @@ -444,7 +528,7 @@ public class WaterLevelService extends Service { } public static void startQtAndroidService(QtActivity activity) { - Log.d("UDP Service", "Starting the service"); + Log.d("UDP Service MainThread", "Starting the service"); Intent intent = new Intent(activity, WaterLevelService.class); activity.startForegroundService(intent); diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java index 84a36f6..9dccce9 100644 --- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java +++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java @@ -10,9 +10,51 @@ import android.app.ActivityManager.RunningServiceInfo; import org.qtproject.qt.android.bindings.QtActivity; import android.os.Handler; import android.os.Looper; +import android.content.ServiceConnection; +import android.content.ComponentName; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; public class WaterLevelServiceStarter { + private static Messenger mServiceMessenger = null; + private static boolean mBound = false; + private static boolean restart_bind = false; + private static ServiceConnection connection = new ServiceConnection() { + + @Override + public void onServiceConnected(ComponentName className, IBinder service) { + mServiceMessenger = new Messenger(service); + mBound = true; + Log.d("UDP Service StarterThread", "Connected to Service object reference"); + if(restart_bind) + { + sendRestartMessage(); + Log.d("UDP Service StarterThread", "Restart Threads in Service."); + } + restart_bind = false; + } + + @Override + public void onServiceDisconnected(ComponentName arg0) { + mServiceMessenger = null; + mBound = false; + } + }; + + public static void startAndBindService(Context context) { + Intent intent = new Intent(context, WaterLevelService.class); + context.startForegroundService(intent); + context.bindService(intent, connection, Context.BIND_AUTO_CREATE); + } + + public static void bindService(Context context) { + restart_bind = true; + Intent intent = new Intent(context, WaterLevelService.class); + context.bindService(intent, connection, Context.BIND_AUTO_CREATE); + } private static boolean isMyServiceRunning(Class serviceClass, Context context) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); @@ -24,6 +66,7 @@ public class WaterLevelServiceStarter { return false; } + private static int getServicePid(Class serviceClass, Context context) { ActivityManager manager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) { @@ -34,6 +77,20 @@ public class WaterLevelServiceStarter { return 0; } + + public static void sendRestartMessage() { + if (!mBound || mServiceMessenger == null) { + Log.d("UDP Service StarterThread", "Service is not running."); + } + try { + Message msg = Message.obtain(null, WaterLevelService.MSG_RESTART_THREADS); + mServiceMessenger.send(msg); + Log.d("UDP Service StarterThread", "Sent restart signal to remote process"); + } catch (RemoteException e) { + Log.e("UDP Service StarterThread", "Failed to send message", e); + } + } + public static void startService(Context context) { // Log.d("UDP Service", "Starting service via QtActivity instance"); @@ -45,15 +102,16 @@ public class WaterLevelServiceStarter { new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { + if (!isMyServiceRunning(WaterLevelService.class, context)) { - Log.d("UDP Service", "Starting service safely on Main Looper"); - Intent intent = new Intent(context, WaterLevelService.class); - context.startForegroundService(intent); + Log.d("UDP Service StarterThread", "Starting service safely on Main Looper"); + startAndBindService(context); } else { - Log.d("UDP Service", "Service already running"); + Log.d("UDP Service StarterThread", "Service already running, so restarting the threads."); + bindService(context); } - Log.d("UDP Service", "Background service running with pid: " + getServicePid(WaterLevelService.class, context)); + Log.d("UDP Service StarterThread", "Background service running with pid: " + getServicePid(WaterLevelService.class, context)); } }); } diff --git a/udplistener.cpp b/udplistener.cpp index e44a9eb..155df4e 100644 --- a/udplistener.cpp +++ b/udplistener.cpp @@ -11,6 +11,7 @@ UDPListener::UDPListener(QObject *parent) connect(worker, &Worker::lastUpdateddatagramReceived, this, &UDPListener::lastUpdateddatagramReceived); connect(worker, &Worker::datagramReceived, this, &UDPListener::datagramRecieved); connect(worker, &Worker::socketError, this, &UDPListener::socketError); + connect(worker, &Worker::sendConfig, this, &UDPListener::sendConfigOnSet); connect(this, &UDPListener::startWorker, worker, &Worker::run); connect(this, &UDPListener::changeWorkerMode, worker, &Worker::changeMode); thread->start(); @@ -33,52 +34,15 @@ void UDPListener::setDistance(double newDistance) emit distanceChanged(); } -void UDPListener::moveAppToBackground() +void UDPListener::sendConfigOnSet() { -#ifdef Q_OS_ANDROID - service->moveAppToBackground(); -#endif -} - -void UDPListener::lastUpdateddatagramReceived(QByteArray data) -{ - QString s_data = QString::fromUtf8(data); - qDebug() << "Main Class last updated on: " << s_data; - QDateTime time = QDateTime::fromString(s_data, "dd/MM/yy hh:mm:ss"); - QDateTime thres_time = QDateTime::currentDateTime().addSecs(-1800); - if(time.isValid() && time < thres_time) - { - emit lastUpdatedChanged(s_data, false); - } - else if(time > thres_time && time.isValid()) { - emit lastUpdatedChanged(s_data, true); - } - else if(!time.isValid()) { - emit lastUpdatedChanged("Invalid Date", true); - } -} - -void UDPListener::continueDestruction() -{ - destroyed = true; -} - -void UDPListener::resetAverages() -{ - averages.clear(); -} - -void UDPListener::setFlipBits(bool flip_bits) -{ - m_flip_bits = flip_bits; -} - -void UDPListener::setConfig(float distance, bool flip_bits, QString mode, QString url) -{ - setFlipBits(flip_bits); + bool flip_bits = m_flip_bits; + QString url = m_url; + QString mode = m_mode; + float distance = m_distance; if(!command_socket) { command_socket = new QUdpSocket(this); - bool bind = command_socket->bind(8181); + bool bind = command_socket->bind(QHostAddress::Any, 0); if(bind) { QNetworkDatagram data1, data2, data3, data4; data1.setDestination(QHostAddress::LocalHost, 8178); @@ -122,12 +86,63 @@ void UDPListener::setConfig(float distance, bool flip_bits, QString mode, QStrin command_socket->deleteLater(); command_socket = nullptr; + qDebug() << "Sent all Command Statements."; emit changeWorkerMode(mode, url); } else { showError("Command Socket Bind Failed"); } } +} + +void UDPListener::moveAppToBackground() +{ +#ifdef Q_OS_ANDROID + service->moveAppToBackground(); +#endif +} + +void UDPListener::lastUpdateddatagramReceived(QByteArray data) +{ + QString s_data = QString::fromUtf8(data); + qDebug() << "Main Class last updated on: " << s_data; + QDateTime time = QDateTime::fromString(s_data, "dd/MM/yy hh:mm:ss").addYears(100); + QDateTime thres_time = QDateTime::currentDateTime().addSecs(-1800); + if(time.isValid() && thres_time < time) + { + emit lastUpdatedChanged(s_data, false); + } + else if(thres_time > time && time.isValid()) { + emit lastUpdatedChanged(s_data, true); + } + else if(!time.isValid()) { + emit lastUpdatedChanged("Invalid Date", true); + } +} + +void UDPListener::continueDestruction() +{ + destroyed = true; +} + +void UDPListener::resetAverages() +{ + averages.clear(); +} + +void UDPListener::setFlipBits(bool flip_bits) +{ + m_flip_bits = flip_bits; +} + +void UDPListener::setConfig(float distance, bool flip_bits, QString mode, QString url) +{ + setFlipBits(flip_bits); + setDistance(distance); + setMode(mode); + setUrl(url); + emit changeWorkerMode(mode, url); + qDebug() << "Setting config from UI completed." << flip_bits << distance << mode << url; averages.clear(); } @@ -279,6 +294,11 @@ void Worker::stop() socket->close(); delete socket; } + if(socket_3) { + disconnect(socket_3, nullptr, nullptr, nullptr); + socket_3->close(); + delete socket_3; + } if(httpTimer) { disconnect(httpTimer, nullptr, nullptr, nullptr); delete httpTimer; @@ -295,27 +315,36 @@ void Worker::run() using namespace std::chrono_literals; socket = new QUdpSocket(this); socket_2 = new QUdpSocket(this); + socket_3 = new QUdpSocket(this); httpTimer = new QTimer(this); manager = new QNetworkAccessManager(this); #ifdef Q_OS_ANDROID // If it is android use it as a command socket; - bool bind = socket->bind(QHostAddress::Any, 8180, QUdpSocket::ReuseAddressHint); - bool bind2 = socket_2->bind(QHostAddress::Any, 8185, QUdpSocket::ReuseAddressHint); + bool bind = socket->bind(QHostAddress::LocalHost, 8180, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress); + bool bind2 = socket_2->bind(QHostAddress::LocalHost, 8185, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress); + bool bind3 = socket_3->bind(QHostAddress::LocalHost, 8186, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress); #else // Else use it as a receiver; bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176); bool bind2 = true; #endif - if(!bind || !bind2) + if(!bind || !bind2 || !bind3) { qDebug() << "UI Worker socket bind failed" << bind << socket->errorString(); + qDebug() << "UI Worker socket bind failed" << bind2 << socket_2->errorString(); + qDebug() << "UI Worker socket bind failed" << bind3 << socket_3->errorString(); emit socketError(socket->errorString()); emit socketError(socket_2->errorString()); + emit socketError(socket_3->errorString()); qApp->exit(-1); } connect(httpTimer, &QTimer::timeout, this, &Worker::httpTimerTimeout); connect(socket, &QUdpSocket::readyRead, this, &Worker::socketDataReceived); connect(socket_2, &QUdpSocket::readyRead, this, &Worker::socket2DataReceived); + connect(socket_3, &QUdpSocket::readyRead, this, &Worker::socket3DataReceived); +#ifdef Q_OS_ANDROID + qDebug() << "Waiting for message from service."; +#endif } @@ -333,6 +362,43 @@ void Worker::changeMode(QString mode, QString url) httpTimer->stop(); socketDataReceived(); } + if(socket) { + if(socket->hasPendingDatagrams()) { + socketDataReceived(); + } + } + if(socket_2) { + if(socket_2->hasPendingDatagrams()) { + socket2DataReceived(); + } + } + if(socket_3) { + if(socket_3->hasPendingDatagrams()) { + socket3DataReceived(); + } + } + +} + +void Worker::socket3DataReceived() +{ + if(socket_3->hasPendingDatagrams()) { + while(this->mode == "" && this->url == "") { + QCoreApplication::processEvents(); + // qDebug() << "Worker is waiting for mode to be set."; + } + size_t data_size = socket_3->pendingDatagramSize(); + qDebug() << "Socket 3 has pending data: " << data_size; + if(data_size < 100000) { + char buff[data_size]; + socket_3->readDatagram(buff, data_size); + QByteArray data = QByteArray::fromRawData(buff, data_size); + if(QString::fromUtf8(data) == "") { + qDebug() << "Data Received from Service Threads."; + emit sendConfig(); + } + } + } } void Worker::socket2DataReceived() @@ -349,6 +415,9 @@ void Worker::socket2DataReceived() else if(mode == "udp") { qDebug() << "Last Updated on Socket data received in udp mode ignoring it."; } + else { + qDebug() << "Mode is not set in socket 2"; + } } } @@ -373,5 +442,34 @@ void Worker::socketDataReceived() else if(mode == "http") { qDebug() << "Socket data received in http mode ignoring it."; } + else { + qDebug() << "Mode is not set in socket."; + } } } + +QString UDPListener::mode() const +{ + return m_mode; +} + +void UDPListener::setMode(const QString &newMode) +{ + if (m_mode == newMode) + return; + m_mode = newMode; + emit modeChanged(); +} + +QString UDPListener::url() const +{ + return m_url; +} + +void UDPListener::setUrl(const QString &newUrl) +{ + if (m_url == newUrl) + return; + m_url = newUrl; + emit urlChanged(); +} diff --git a/udplistener.h b/udplistener.h index 8c123c8..079dd35 100644 --- a/udplistener.h +++ b/udplistener.h @@ -25,6 +25,7 @@ class Worker: public QObject { Q_OBJECT QUdpSocket *socket = nullptr; QUdpSocket *socket_2 = nullptr; + QUdpSocket *socket_3 = nullptr; QString mode = ""; QString url = ""; QNetworkAccessManager *manager = nullptr; @@ -32,6 +33,7 @@ class Worker: public QObject { public: explicit Worker(QObject *parent = nullptr); signals: + void sendConfig(); void stopped(); void socketError(QString error); void lastUpdateddatagramReceived(QByteArray data); @@ -43,6 +45,7 @@ public slots: void run(); void changeMode(QString mode, QString url); private slots: + void socket3DataReceived(); void socket2DataReceived(); void socketDataReceived(); }; @@ -52,6 +55,8 @@ class UDPListener : public QObject Q_OBJECT QML_ELEMENT Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged FINAL) + Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged FINAL) + Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged FINAL) bool destroyed = false; #ifdef Q_OS_ANDROID service *service = nullptr; @@ -60,6 +65,12 @@ public: explicit UDPListener(QObject *parent = nullptr); double distance() const; void setDistance(double newDistance); + QString mode() const; + void setMode(const QString &newMode); + + QString url() const; + void setUrl(const QString &newUrl); + signals: void lastUpdatedChanged(QString last_updated, bool warn); void stopWorker(); @@ -70,7 +81,12 @@ signals: void disconnected(); void distanceReceived(double dist, QString timestamp); void changeWorkerMode(QString mode, QString url); + void modeChanged(); + void initLoadFinished(); + void urlChanged(); + public slots: + void sendConfigOnSet(); void moveAppToBackground(); void lastUpdateddatagramReceived(QByteArray data); void continueDestruction(); @@ -93,6 +109,8 @@ private: QThread *thread = nullptr; QTimer *timer = nullptr; bool m_flip_bits; + QString m_mode; + QString m_url; }; #endif // UDPLISTENER_H