diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b80b0d..45891ca 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 CorePrivate) +find_package(Qt6 REQUIRED COMPONENTS Quick Network) -qt_standard_project_setup(REQUIRES 6.11) +qt_standard_project_setup(REQUIRES 6.9) qt_add_executable(appwaterLevelSensor main.cpp @@ -16,22 +16,10 @@ qt_add_executable(appwaterLevelSensor if(ANDROID) - -include(FetchContent) -FetchContent_Declare( - android_openssl - DOWNLOAD_EXTRACT_TIMESTAMP true - URL https://github.com/KDAB/android_openssl/archive/refs/heads/master.zip -) -FetchContent_MakeAvailable(android_openssl) -include(${android_openssl_SOURCE_DIR}/android_openssl.cmake) - qt_add_library(service SHARED servicemain.cpp service.h service.cpp ) - - set_property(TARGET appwaterLevelSensor APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/android) set_property(TARGET service APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR @@ -41,8 +29,8 @@ set_property(TARGET service APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR target_link_libraries(service PRIVATE Qt::Core - Qt::Network Qt::CorePrivate + Qt::Network Qt::Quick ) qt_add_qml_module(appwaterLevelSensor @@ -57,7 +45,6 @@ qt_add_qml_module(appwaterLevelSensor SOURCES service.h RESOURCES README.md ) -add_android_openssl_libraries(service appwaterLevelSensor) target_link_libraries(appwaterLevelSensor PRIVATE Qt6::Quick Qt6::Network service ) diff --git a/Main.qml b/Main.qml index e7844b9..66f221e 100644 --- a/Main.qml +++ b/Main.qml @@ -6,62 +6,19 @@ import QtQuick.Controls.Material Window { width: 640 - height: 1080 + height: 480 visible: true title: qsTr("Hello World") id: root property color accent_color: "#F05FAF5F" - property color dark_accent_color: "#FF5F7F5F" property color danger_color: "#F0AF2F1F" property color water_color: "#F00F7FAF" Material.accent: root.accent_color property int font_enlarged_text_size: 20 - property int font_normal_text_size: 16 + property int font_normal_text_size: 18 property double distance: 0 property int distance_count: 0 - property int distance_count_thres: 3 - property string mode: "http" // visibility: Window.FullScreen - Component.onCompleted: { - 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 { anchors.fill: parent id: background @@ -188,8 +145,8 @@ Window { border.color: root.accent_color border.width: 1 } - height: Math.max(parent.height / 1.2, contentHeight) - width: parent.width / 1.1 + height: parent.height / 1.5 + width: parent.width / 1.5 anchors.centerIn: Overlay.overlay @@ -219,26 +176,24 @@ Window { topMargin: 20 top: settings_label.bottom left: parent.left - verticalCenter: max_thres_input.verticalCenter } height: max_thres_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color - font.pointSize: root.font_normal_text_size + font.pixelSize: root.font_normal_text_size } TextField { id: max_thres_input text: config.max anchors { - right: parent.right - rightMargin: 5 + left: max_thres_label.right + leftMargin: 5 top: settings_label.bottom topMargin: 20 } - width: parent.width / 2 color: root.accent_color - font.pointSize: root.font_normal_text_size + font.pixelSize: root.font_normal_text_size } @@ -249,26 +204,24 @@ Window { topMargin: 20 top: max_thres_label.bottom left: parent.left - verticalCenter: min_thres_input.verticalCenter } height: min_thres_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color - font.pointSize: root.font_normal_text_size + font.pixelSize: root.font_normal_text_size } TextField { id: min_thres_input text: config.min anchors { - right: parent.right - rightMargin: 5 + left: min_thres_label.right + leftMargin: 5 top: max_thres_label.bottom topMargin: 20 } - width: parent.width / 2 color: root.accent_color - font.pointSize: root.font_normal_text_size + font.pixelSize: root.font_normal_text_size } @@ -279,132 +232,24 @@ Window { topMargin: 20 top: min_thres_label.bottom left: parent.left - verticalCenter: alert_thres_input.verticalCenter } height: alert_thres_input.height verticalAlignment: Text.AlignVCenter color: root.accent_color - font.pointSize: root.font_normal_text_size + font.pixelSize: root.font_normal_text_size } TextField { id: alert_thres_input text: config.alert anchors { - right: parent.right - rightMargin: 5 + left: alert_thres_label.right + leftMargin: 5 top: min_thres_label.bottom topMargin: 20 } - width: parent.width / 2 color: root.accent_color - font.pointSize: root.font_normal_text_size - } - CheckBox { - id: flip_bits_box - text: "Flip Bits" - checked: config.flipBits - anchors { - topMargin: 20 - top: alert_thres_label.bottom - horizontalCenter: parent.horizontalCenter - } - contentItem: Text { - text: flip_bits_box.text - font.pointSize: root.font_normal_text_size - color: root.accent_color - leftPadding: flip_bits_box.indicator.width + flip_bits_box.spacing - } - } - - Text { - id: settings_mode_text - anchors { - topMargin: 20 - top: flip_bits_box.bottom - left: parent.left - } - font.pointSize: root.font_normal_text_size - color: root.accent_color - text: "Mode: " - } - Row { - id: mode_row - anchors { - left: settings_mode_text.right - right: parent.right - verticalCenter: settings_mode_text.verticalCenter - leftMargin: 10 - } - RadioButton { - id: udp_radio_button - checked: config.mode === "udp" ? true : false - text: "UDP" - font.pointSize: root.font_normal_text_size - contentItem: Text { - text: udp_radio_button.text - font: udp_radio_button.font - opacity: enabled ? 1.0 : 0.3 - color: udp_radio_button.down ? root.dark_accent_color : root.accent_color - verticalAlignment: Text.AlignVCenter - leftPadding: udp_radio_button.indicator.width + udp_radio_button.spacing - } - onCheckedChanged: { - if(checked) { - root.mode = "udp"; - } - } - } - RadioButton { - id: http_radio_button - checked: config.mode === "http" ? true : false - text: "HTTP" - font.pointSize: root.font_normal_text_size - contentItem: Text { - text: http_radio_button.text - font: http_radio_button.font - opacity: enabled ? 1.0 : 0.3 - color: http_radio_button.down ? root.dark_accent_color : root.accent_color - verticalAlignment: Text.AlignVCenter - leftPadding: http_radio_button.indicator.width + http_radio_button.spacing - } - onCheckedChanged: { - if(checked) { - root.mode = "http"; - } - } - } - } - Text { - id: http_url_label - text: "Url: " - anchors { - topMargin: 20 - top: settings_mode_text.bottom - left: parent.left - verticalCenter: http_url_input.verticalCenter - } - height: http_url_input.height - verticalAlignment: Text.AlignVCenter - color: root.accent_color - font.pointSize: root.font_normal_text_size - } - - TextField { - id: http_url_input - text: config.url - anchors { - right: parent.right - rightMargin: 20 - leftMargin: 20 - top: mode_row.bottom - topMargin: 20 - } - enabled: root.mode === "http" ? true : false - width: Math.max(parent.width / 2, parent.width - http_url_label.width - anchors.leftMargin) - color: root.accent_color - font.pointSize: root.font_normal_text_size - cursorPosition: 0 + font.pixelSize: root.font_normal_text_size } Text { @@ -414,9 +259,7 @@ Window { visible: settings_error_text === "" ? false : true anchors { topMargin: 20 - top: http_url_label.bottom - bottom: reset_averages.top - bottomMargin: 10 + top: alert_thres_label.bottom left: parent.left horizontalCenter: parent.horizontalCenter } @@ -425,52 +268,7 @@ Window { font.pixelSize: root.font_normal_text_size } - Rectangle { - id: reset_averages - anchors { - topMargin: 10 - bottom: save_config.top - bottomMargin: 10 - horizontalCenter: parent.horizontalCenter - } - color: "#F0121212" - width: parent.width / 1.3 - height: 60 - border { - width: 1 - color: root.accent_color - } - radius: 3 - Text { - anchors.centerIn: parent - text: "Reset Averages" - color: root.accent_color - font.pointSize: root.font_enlarged_text_size - } - MouseArea { - anchors.fill: parent - hoverEnabled: true - property color settings_rect_pressed_prev: "#00000000" - property color settings_rect_entered_prev: "#00000000" - onEntered: { - settings_rect_entered_prev = reset_averages.color - reset_averages.color = "#5A1F1F1F" - } - onExited: { - reset_averages.color = settings_rect_entered_prev - } - onPressed: { - settings_rect_pressed_prev = reset_averages.color - reset_averages.color = "#5A1F4F1F" - listener.resetAverages(); - } - onReleased: { - reset_averages.color = settings_rect_pressed_prev - } - } - - } Rectangle { id: save_config @@ -480,7 +278,7 @@ Window { horizontalCenter: parent.horizontalCenter } color: "#F0121212" - width: parent.width / 1.3 + width: parent.width / 2.5 height: 60 border { width: 1 @@ -512,10 +310,7 @@ Window { let max = parseFloat(min_thres_input.text); let min = parseFloat(min_thres_input.text); let alert = parseFloat(alert_thres_input.text); - let flipBits = flip_bits_box.checked; - let mode = root.mode; - let url = http_url_input.text; - console.log("Parsed values:", max, min, alert, flip_bits_box.checked); + console.log("Parsed values:", max, min, alert); if(max < min) { settings_error_label.settings_error_text = "Max cannot be less than min"; return; @@ -524,19 +319,20 @@ Window { settings_error_label.settings_error_text = "Alert must be between 0 to 100"; return; } + + 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_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading; console.log("Alert effective reading is: " + alert_effective_reading + " for value: " + alert); - listener.setConfig(alert_effective_reading, flipBits, mode, url); - config.saveConfig(max_thres_input.text, min_thres_input.text, alert_thres_input.text, flip_bits_box.checked, mode, url); + listener.setAlert(alert_effective_reading); + config.saveConfig(max_thres_input.text, min_thres_input.text, alert_thres_input.text); settings_popup.close(); } onReleased: { save_config.color = settings_rect_pressed_prev } } - } } } @@ -558,7 +354,7 @@ Window { id: tank_base color: "#0FFFFFFF" anchors.centerIn: parent - width: parent.width / 2 + width: parent.width / 2.5 height: parent.height / 2 border.color: "#F0121212" border.width: 5 @@ -614,20 +410,6 @@ Window { color: root.accent_color font.pixelSize: root.font_normal_text_size } - Text { - id: last_updated_text - property string last_updated: "" - text: "Last Updated On Server On: " + last_updated - anchors { - top: info_percent_text.bottom - topMargin: 10 - horizontalCenter: parent.horizontalCenter - } - horizontalAlignment: Text.AlignHCenter - color: root.accent_color - font.pixelSize: root.font_normal_text_size - visible: config.mode === "http" ? true : false - } Text { id: error_text @@ -635,7 +417,7 @@ Window { text: "Error: " + error_string visible: error_string === "" ? false : true; anchors { - top: last_updated_text.bottom + top: info_percent_text.bottom topMargin: 10 horizontalCenter: parent.horizontalCenter } @@ -662,40 +444,11 @@ Window { ConfigInterface { id: config - onConfigData: (max, min, alert, mode, url) => { - console.log("Got config data as: ", max, min, alert, mode, url); - } - onModeChanged: { - root.mode = mode; + onConfigData: (max, min, alert) => { + console.log("Got config data as: ", max, min, alert); } } - Timer { - property int count: 0 - id: init_timer - running: true - repeat: true - interval: 1000 - onTriggered: { - 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(); - } - } - } UDPListener { id: listener @@ -704,29 +457,12 @@ Window { error_text.error_string = err; } - onLastUpdatedChanged: (update, warn) => { - console.log("QML 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: { console.log("Distance changed"); root.distance = listener.distance; error_text.error_string = ""; console.log("Distance count: ", root.distance_count); - if(root.distance_count >= root.distance_count_thres) { - if(loading_popup.opened) { - loading_popup.close(); - } + if(root.distance_count >= 5) { 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); @@ -742,9 +478,7 @@ Window { else { water_level.color = root.water_color; } - if(root.distance_count > 1000) { - root.distance_count = root.distance_count_thres; - } + console.log("Current reading is: " + current_reading) console.log("Normalized height is: " + normalized_height) water_level.current_level = normalized_height; @@ -773,29 +507,23 @@ Window { let alert = parseFloat(alert_thres_input.text); let alert_effective_reading = (effective_max_reading * (100 - alert) / 100) + current_min_reading; console.log("Alert effective reading is: " + alert_effective_reading + " for value: " + alert); - listener.setConfig(alert_effective_reading, flip_bits_box.checked, root.mode, http_url_input.text); + listener.setAlert(alert_effective_reading); } distance_count += 1; } } - onClosing: (close)=> { + onClosing:(close)=> { if(Qt.platform.os === "android"){ - // close.accepted = false - // listener.moveAppToBackground(); - // console.log("Moving to backgound"); - close.accepted = true; + close.accepted = false } else { close.accepted = true } } function dataBlink() { - if(status_indicator_timer.running) { - status_indicator_timer.stop(); - } - let old_color = "#F05F5F0F"; + let old_color = status_indicator.color; let new_color = "#F04FAF5F"; status_indicator.color = new_color; status_indicator.old_color = old_color; diff --git a/README.md b/README.md index 7198d51..96a9151 100644 --- a/README.md +++ b/README.md @@ -5,13 +5,4 @@ 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.
- - -
-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 +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 diff --git a/android/AndroidManifest.xml b/android/AndroidManifest.xml index ead6548..d31ff4c 100644 --- a/android/AndroidManifest.xml +++ b/android/AndroidManifest.xml @@ -11,7 +11,7 @@ - + - + android:foregroundServiceType="specialUse" + android:exported="true"> + diff --git a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java index cc765d0..c868acc 100644 --- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java +++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java @@ -19,190 +19,39 @@ import org.qtproject.qt.android.bindings.QtActivity; import android.app.Service; import java.time.Instant; import java.time.Duration; -import java.io.BufferedReader; -import java.io.InputStreamReader; -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 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; - - - 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 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; - private boolean received_cmd_first = false; - @Override public void run() { - Log.d("UDP Service CommandThread", "Command thread initialising"); + Log.d("UDP Service", "Command thread initialising"); try { - 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(8187)); - boolean not_send_set = true; - Log.d("UDP Service CommandThread", "Created command thread sockets"); + socket_cmd_recv = new DatagramSocket(8178, InetAddress.getByName("localhost")); + socket_cmd_send = new DatagramSocket(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(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 { - socket_cmd_recv.receive(packet); - } catch(java.net.SocketTimeoutException e) { - continue; - } + socket_cmd_recv.receive(packet); String str = new String(buffer, StandardCharsets.UTF_8); - Log.d("UDP Service CommandThread", "Received command packet " + str); - not_send_set = false; if(str.startsWith("", ""); alert_thres_distance = Float.parseFloat(str); - { - if(!checkBound()) - { - setBound(true); - Log.d("UDP Service CommandThread", "Set Bound to True"); - } - else { - Log.d("UDP Service CommandThread", "Set Bound is False"); - } + } + Log.d("UDP Service ", "Received command packet " + str); - } - } - else if(str.startsWith("", ""); - Log.d("UDP Service CommandThread", "Flipping " + str); - if(str.toLowerCase() == "false") { - bits_flipped = false; - } - else if(str.toLowerCase() == "true") { - bits_flipped = true; - } - } - else if(str.startsWith("", ""); - Log.d("UDP Service CommandThread", "Mode " + str); - mode = str.toLowerCase(); - } - else if(str.startsWith("", ""); - Log.d("UDP Service CommandThread", "Url " + str); - url = str.toLowerCase(); - } } } catch (IOException e) { - // showToast("IOException: " + e); - Log.e("UDP Service CommandThread", "IOException Error" + e); + Log.e("UDP Service", "Socket Error", e); } } @@ -212,15 +61,13 @@ 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 ClientThread", "Client Thread initialising"); + Log.d("UDP Service", "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"); android.os.PowerManager pm = (android.os.PowerManager) getSystemService(Context.POWER_SERVICE); android.os.PowerManager.WakeLock wakeLock = pm.newWakeLock(android.os.PowerManager.PARTIAL_WAKE_LOCK, "WaterLevel:WakeLock"); @@ -237,144 +84,17 @@ public class WaterLevelService extends Service { try { multicastLock.acquire(); wakeLock.acquire(); - wifiLock.acquire(); - socket_recv = new DatagramSocket(null); - socket_send = new DatagramSocket(null); + socket_recv = new DatagramSocket(8176); + socket_send = new DatagramSocket(8177); socket_recv.setBroadcast(true); - socket_recv.setSoTimeout(4000); - socket_recv.setReuseAddress(true); - socket_send.setReuseAddress(true); - socket_send.bind(new InetSocketAddress(8177)); - socket_recv.bind(new InetSocketAddress(8176)); - Log.d("UDP Service ClientThread", "Created Sockets"); - int count = 0; + Log.d("UDP Service", "Created Sockets"); while (!Thread.currentThread().isInterrupted()) { - count++; - if(count == 3) { - Log.i("UDP Service ClientThread", "Thread is running with mode: " + mode); - count = 0; - } - if(checkBound()) { - 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; - socket_send = null; - socket_recv = new DatagramSocket(null); - socket_send = new DatagramSocket(null); - socket_recv.setBroadcast(true); - socket_recv.setSoTimeout(4000); - socket_recv.setReuseAddress(true); - socket_send.setReuseAddress(true); - socket_send.bind(new InetSocketAddress(8177)); - socket_recv.bind(new InetSocketAddress(8176)); - setBound(false); - Log.i("UDP Service ClientThread", "Reconnected sockets"); - } - // if(checkBound()) { - // Log.i("UDP Service", "Reset socket binds: " + mode); - // socket_recv.close(); - // socket_send.close(); - // socket_send.bind(new InetSocketAddress(8177)); - // socket_recv.bind(new InetSocketAddress(8176)); - // setBound(false); - // if(socket_send.isBound()) { - // Log.i("UDP Service", "Rebound socket: " + 8177); - // } - // else { - // Log.i("UDP Service", "Failed to rebound socket: " + 8177); - // } - // if(socket_recv.isBound()) { - // Log.i("UDP Service", "Rebound socket: " + 8176); - // } - // else { - // Log.i("UDP Service", "Failed to rebound socket: " + 8176); - // } - - // } - // if(checkBound()) { - // Log.i("UDP Service", "Reset socket binds: " + mode); - // } - // else { - // Log.i("UDP Service", "Reset socket binds: " + mode); - // } - byte[] buffer = null; - String updated = ""; - if(mode.equals("udp")) { - try { - if(socket_recv.isClosed()) { - socket_recv.bind(new InetSocketAddress(8176)); - } - buffer = new byte[4]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - socket_recv.receive(packet); - } catch(java.net.SocketTimeoutException e) { - // showToast("Socket Timeout: " + e); - continue; - } - } - else if(mode.equals("http")) { - try { - Thread.sleep(2000); - } catch (InterruptedException e) { - Log.d("UDP Service ClientThread", "Thread exiting due to interrupt."); - return; - } - Log.d("UDP Service ClientThread", "Http String Url: " + url); - URL t_url = new URL(url); - HttpURLConnection con = (HttpURLConnection) t_url.openConnection(); - con.setRequestMethod("GET"); - con.setConnectTimeout(5000); - con.setReadTimeout(5000); - con.setInstanceFollowRedirects(true); - int status = con.getResponseCode(); - 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(); - try { - JSONObject obj = new JSONObject(data); - JSONArray arr = obj.getJSONArray("water_level"); - float f = (float ) arr.getDouble(0); - updated = obj.getString("updated_on"); - buffer = ByteBuffer.allocate(4).putFloat(f).array(); - 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 ClientThread", "Invalid Json"); - // showToast("Invalid Json" + e); - continue; - } - } - else { - // showToast("Bad Response From Server: " + status); - InputStreamReader streamReader = new InputStreamReader(con.getErrorStream()); - continue; - } - } - else { - Log.d("UDP Service ClientThread", "Invalid mode: " + mode); - // showToast("Invalid mode: " + mode); - try { - Thread.sleep(3000); - } catch (InterruptedException e) { - Log.d("UDP Service ClientThread", "Thread exiting due to interrupt."); - return; - } - continue; - } - float f; - if(bits_flipped) { - f = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getFloat(); - } - else { - f = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN).getFloat(); - } - Log.d("UDP Service ClientThread", "Received packet: " + f); - Log.d("UDP Service ClientThread", "Threshold is: " + alert_thres_distance); + byte[] buffer = new byte[4]; + DatagramPacket packet = new DatagramPacket(buffer, buffer.length); + socket_recv.receive(packet); + float f = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN).getFloat(); + Log.d("UDP Service ", "Received packet: " + f); + Log.d("UDP Service ", "Threshold is: " + alert_thres_distance); boolean duration_passed = false; if(!started) { duration_passed = true; @@ -383,7 +103,7 @@ public class WaterLevelService extends Service { else { Instant currentTimestamp = Instant.now(); Duration duration = Duration.between(prevAlertTimestamp, currentTimestamp); - if(duration.toMinutes() > 60) { + if(duration.toMinutes() > 30) { duration_passed = true; } prevAlertTimestamp = currentTimestamp; @@ -393,33 +113,18 @@ public class WaterLevelService extends Service { .setSmallIcon(android.R.drawable.ic_dialog_alert) .setContentTitle("Water Level Sensor") .setContentText("Water Level is below alert threshold") - .setPriority(Notification.PRIORITY_LOW); + .setPriority(Notification.PRIORITY_HIGH); manager.notify(2, builder.build()); - Log.d("UDP Service ClientThread", "Notification sent"); - if(!started) { - started = true; - } + Log.d("UDP Service", "Notification sent"); } - if(socket_send.isClosed()) { - socket_send.bind(new InetSocketAddress(8177)); - } - DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("127.0.0.1"), 8180); + DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8180); socket_send.send(send_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("127.0.0.1"), 8185); - socket_send.send(send_packet_2); - Log.d("UDP Service ClientThread", "Sent last updated packet"); - } - } + Log.d("UDP Service ", "Sent packet"); + } } catch (IOException e) { - // showToast("IOException: " + e); - Log.e("UDP Service ClientThread", "IOException Error" + e); + Log.e("UDP Service", "Socket Error", e); } finally { - if (wifiLock.isHeld()) - wifiLock.release(); if (multicastLock.isHeld()) multicastLock.release(); if (wakeLock.isHeld()) @@ -431,7 +136,7 @@ public class WaterLevelService extends Service { } public void startWaterLevelService() { - Log.d("UDP Service MainThread", "Starting the service from Activity"); + Log.d("UDP Service", "Starting the service from Activity"); Intent intent = new Intent(this, WaterLevelService.class); @@ -439,25 +144,25 @@ public class WaterLevelService extends Service { } public WaterLevelService() { super(); - Log.i("UDP Service MainThread", "Class is created"); + Log.i("UDP Service", "Class is created"); } @Override - public android.os.IBinder onBind(android.content.Intent intent) { return mMessenger.getBinder(); } + public android.os.IBinder onBind(android.content.Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Notification notification = createNotification(); startForeground(1, notification); - Log.i("UDP Service MainThread", "Creating Service"); + Log.i("UDP Service", "Creating Service"); } @Override public void onDestroy() { super.onDestroy(); - Log.i("UDP Service MainThread", "Destroying Service"); + Log.i("UDP Service", "Destroying Service"); } @Override @@ -465,47 +170,16 @@ public class WaterLevelService extends Service { Notification notification = createNotification(); startForeground(1, notification); - - if (intent == null) { - Log.w("UDP Service MainThread", "Service restarted with null intent. Stopping to avoid crash."); - stopSelf(); - return START_NOT_STICKY; - } - 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", "Error starting threads", e); - } - } - }).start(); - - Log.d("UDP Service MainThread", "Actually starting the thread"); + Log.d("UDP Service", "Actually starting the thread"); int ret = super.onStartCommand(intent, flags, startId); - // (new Thread(new UDPClient())).start(); - // (new Thread(new UDPCommandClient())).start(); + (new Thread(new UDPClient())).start(); + (new Thread(new UDPCommandClient())).start(); - return START_NOT_STICKY; + return START_STICKY; } - public void showToast(String str) { - int duration = Toast.LENGTH_SHORT; - Toast toast = Toast.makeText(this, str, duration); - toast.show(); - } private Notification createNotification() { String channelId = "water_level_channel"; @@ -528,7 +202,7 @@ public class WaterLevelService extends Service { } public static void startQtAndroidService(QtActivity activity) { - Log.d("UDP Service MainThread", "Starting the service"); + Log.d("UDP Service", "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 9dccce9..98b114c 100644 --- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java +++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java @@ -5,114 +5,15 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.util.Log; -import android.app.ActivityManager; -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); - 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 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"); + Log.d("UDP Service", "Starting service via QtActivity instance"); - // if(!isMyServiceRunning(WaterLevelService.class, context)) { - // Intent intent = new Intent(context, WaterLevelService.class); - // context.startForegroundService(intent); - // } + Intent intent = new Intent(context, WaterLevelService.class); - new Handler(Looper.getMainLooper()).post(new Runnable() { - @Override - public void run() { - - if (!isMyServiceRunning(WaterLevelService.class, context)) { - Log.d("UDP Service StarterThread", "Starting service safely on Main Looper"); - startAndBindService(context); - } - else { - Log.d("UDP Service StarterThread", "Service already running, so restarting the threads."); - bindService(context); - } - Log.d("UDP Service StarterThread", "Background service running with pid: " + getServicePid(WaterLevelService.class, context)); - } - }); + context.startForegroundService(intent); } } diff --git a/configinterface.cpp b/configinterface.cpp index 73ae20a..563cf68 100644 --- a/configinterface.cpp +++ b/configinterface.cpp @@ -21,30 +21,20 @@ ConfigInterface::ConfigInterface(QObject *parent) auto data = config.readAll(); config.close(); QJsonObject top = QJsonDocument::fromJson(data).object(); - QString max, min, alert, mode, url; - bool flipbits; - 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"); + QString max, min, alert; + max = top["max_threshold"].toString(); + min = top["min_threshold"].toString(); + alert = top["alert_threshold"].toString(); setMax(max); setMin(min); setAlert(alert); - setFlipBits(flipbits); - setMode(mode); - setUrl(url); } else { qDebug() << "File does not exist creating one..."; QJsonObject top; - 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"; + top["max_threshold"] = "125"; + top["min_threshold"] = "25"; + top["alert_threshold"] = "100"; QByteArray data = QJsonDocument(top).toJson(); bool ok = config.open(QFile::WriteOnly); if(!ok) { @@ -54,17 +44,10 @@ ConfigInterface::ConfigInterface(QObject *parent) config.write(data); config.flush(); config.close(); - setMax("110"); - setMin("10"); - setAlert("30"); - setFlipBits(true); - setMode("http"); - setUrl("https://unrealdragon.in/server/waterlevel"); - qDebug() << "Config set initial values completed"; } } -void ConfigInterface::saveConfig(QString max, QString min, QString alert, bool flipbits, QString mode, QString url) +void ConfigInterface::saveConfig(QString max, QString min, QString alert) { auto paths = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); qDebug() << "Got writable path as: " << paths; @@ -75,13 +58,9 @@ void ConfigInterface::saveConfig(QString max, QString min, QString alert, bool f top["max_threshold"] = max; top["min_threshold"] = min; top["alert_threshold"] = alert; - top["flip_bits"] = flipbits; - top["mode"] = mode; - top["url"] = url; setMax(max); setMin(min); setAlert(alert); - setMode(mode); QByteArray data = QJsonDocument(top).toJson(); config.open(QIODevice::WriteOnly); config.write(data); @@ -127,42 +106,3 @@ void ConfigInterface::setMin(const QString &newMin) m_min = newMin; emit minChanged(); } - -bool ConfigInterface::flipBits() const -{ - return m_flipBits; -} - -void ConfigInterface::setFlipBits(bool newFlipBits) -{ - if (m_flipBits == newFlipBits) - return; - m_flipBits = newFlipBits; - emit flipBitsChanged(); -} - -QString ConfigInterface::mode() const -{ - return m_mode; -} - -void ConfigInterface::setMode(const QString &newMode) -{ - if (m_mode == newMode) - return; - m_mode = newMode; - emit modeChanged(); -} - -QString ConfigInterface::url() const -{ - return m_url; -} - -void ConfigInterface::setUrl(const QString &newUrl) -{ - if (m_url == newUrl) - return; - m_url = newUrl; - emit urlChanged(); -} diff --git a/configinterface.h b/configinterface.h index 840cc2c..edf60ee 100644 --- a/configinterface.h +++ b/configinterface.h @@ -17,9 +17,6 @@ class ConfigInterface : public QObject Q_PROPERTY(QString min READ min WRITE setMin NOTIFY minChanged FINAL) Q_PROPERTY(QString max READ max WRITE setMax NOTIFY maxChanged FINAL) Q_PROPERTY(QString alert READ alert WRITE setAlert NOTIFY alertChanged FINAL) - Q_PROPERTY(QString mode READ mode WRITE setMode NOTIFY modeChanged FINAL) - Q_PROPERTY(QString url READ url WRITE setUrl NOTIFY urlChanged FINAL) - Q_PROPERTY(bool flipBits READ flipBits WRITE setFlipBits NOTIFY flipBitsChanged FINAL) public: explicit ConfigInterface(QObject *parent = nullptr); QString alert() const; @@ -31,33 +28,18 @@ public: QString min() const; void setMin(const QString &newMin); - bool flipBits() const; - void setFlipBits(bool newFlipBits); - - QString mode() const; - void setMode(const QString &newMode); - - QString url() const; - void setUrl(const QString &newUrl); - signals: - void configData(QString max, QString min, QString alert, bool flipbits, QString mode, QString url); + void configData(QString max, QString min, QString alert); void alertChanged(); void maxChanged(); void minChanged(); - void flipBitsChanged(); - void modeChanged(); - void urlChanged(); public slots: - void saveConfig(QString max, QString min, QString alert, bool flipbits, QString mode, QString url); + void saveConfig(QString max, QString min, QString alert); private: QString m_alert; QString m_max; QString m_min; - bool m_flipBits; - QString m_mode; - QString m_url; }; #endif // CONFIGINTERFACE_H diff --git a/moving_container.h b/moving_container.h index 966bce8..89a1cf0 100644 --- a/moving_container.h +++ b/moving_container.h @@ -1,7 +1,7 @@ #ifndef MOVING_CONTAINER #define MOVING_CONTAINER -#include + using ld = long double; template class average { @@ -21,16 +21,11 @@ public: ~average() { curr = head; - if(!curr) - return; while(curr != nullptr) { info *now = curr; curr = curr->next; delete now; } - curr = nullptr; - head = nullptr; - tail = nullptr; } unsigned int size() { @@ -41,21 +36,6 @@ public: return this->avg; } - void clear() { - curr = head; - if(!curr) - return; - while(curr != nullptr) { - info *now = curr; - curr = curr->next; - delete now; - } - curr = nullptr; - head = nullptr; - tail = nullptr; - count = 0; - } - void recalculate_average() { curr = head; this->avg = 0; diff --git a/service.cpp b/service.cpp index f48e455..6b819b0 100644 --- a/service.cpp +++ b/service.cpp @@ -11,6 +11,7 @@ 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) { @@ -28,95 +29,87 @@ 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; - // // 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; + 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(); - // 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()); - // } + // 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); } } - -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 1ba18a1..5b64765 100644 --- a/service.h +++ b/service.h @@ -8,8 +8,6 @@ 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 155df4e..4978794 100644 --- a/udplistener.cpp +++ b/udplistener.cpp @@ -1,19 +1,19 @@ #include "udplistener.h" #include +#ifdef Q_OS_ANDROID +#include "service.h" +#endif UDPListener::UDPListener(QObject *parent) : QObject{parent} { - worker = new Worker(); - thread = new QThread(); + worker = new Worker(this); + thread = new QThread(this); worker->moveToThread(thread); - 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(); emit startWorker(); timer = new QTimer(this); @@ -21,6 +21,11 @@ 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 @@ -34,118 +39,26 @@ void UDPListener::setDistance(double newDistance) emit distanceChanged(); } -void UDPListener::sendConfigOnSet() +void UDPListener::setAlert(float distance) { - 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(QHostAddress::Any, 0); + bool bind = command_socket->bind(8181); if(bind) { - QNetworkDatagram data1, data2, data3, data4; - data1.setDestination(QHostAddress::LocalHost, 8178); - data2.setDestination(QHostAddress::LocalHost, 8178); - data3.setDestination(QHostAddress::LocalHost, 8178); - data4.setDestination(QHostAddress::LocalHost, 8178); - + QNetworkDatagram data; + data.setDestination(QHostAddress::LocalHost, 8178); QString s_data = ""; while(s_data.toUtf8().size() != 1024) { s_data += "="; } - data1.setData(s_data.toUtf8()); - command_socket->writeDatagram(data1); + data.setData(s_data.toUtf8()); + command_socket->writeDatagram(data); command_socket->flush(); - - QString b_data = ""; - while(b_data.toUtf8().size() != 1024) { - b_data += "="; - } - data2.setData(b_data.toUtf8()); - command_socket->writeDatagram(data2); - command_socket->flush(); - - QString m_data = ""; - while(m_data.toUtf8().size() != 1024) { - m_data += "="; - } - data3.setData(m_data.toUtf8()); - command_socket->writeDatagram(data3); - command_socket->flush(); - - QString u_data = ""; - while(u_data.toUtf8().size() != 1024) { - u_data += "="; - } - data4.setData(u_data.toUtf8()); - command_socket->writeDatagram(data4); - command_socket->flush(); - command_socket->close(); - 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(); -} - void UDPListener::socketError(QString err) { qDebug() << "Socket error received: " << err; @@ -157,34 +70,15 @@ 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 } -void UDPListener::datagramRecieved(QByteArray data1, QByteArray data2) +void UDPListener::datagramRecieved(float distance) { - std::string correct_order = ""; -#ifdef Q_OS_ANDROID - if(!m_flip_bits) - correct_order = "0x" + data2.toHex().toStdString(); - else - correct_order = "0x" + data1.toHex().toStdString(); -#else - if(!m_flip_bits) - correct_order = "0x" + data1.toHex().toStdString(); - else - correct_order = "0x" + data2.toHex().toStdString(); -#endif - float data = convertToFloat(correct_order); - qDebug() << "Data received in main class: " << data; - averages.push_back(data); + qDebug() << "Data received in main class: " << distance; + averages.push_back(distance); prev_time_point = std::chrono::high_resolution_clock::now(); - emit distanceReceived(data, QDateTime::currentDateTime().toString("dd/mm/yyyy hh:mm:ss.z ap")); + emit distanceReceived(distance, QDateTime::currentDateTime().toString("dd/mm/yyyy hh:mm:ss.z ap")); if(averages.size() >= 5) { this->m_distance = averages.get_average(); emit distanceChanged(); @@ -197,31 +91,11 @@ 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(QEventLoop::AllEvents, 2000); - } thread->exit(0); - QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); - worker->deleteLater(); - QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); - while(thread->isRunning()) { - qDebug() << "The thread is still running."; - QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); - } thread->deleteLater(); - QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); + worker->deleteLater(); timer->deleteLater(); - QCoreApplication::processEvents(QEventLoop::AllEvents, 2000); qDebug() << "Deinitialized UDP Listener"; } @@ -237,239 +111,51 @@ void UDPListener::connectionTimerTimedOut() } } -float UDPListener::convertToFloat(std::string hex) -{ - uint32_t num; - sscanf(hex.c_str(), "%x", &num); - float dist = *((float*)&num); - return dist; -} - Worker::Worker(QObject *parent) { } -void Worker::gotHttpReply(QNetworkReply *rep) -{ - QByteArray m_data = rep->readAll(); - rep->deleteLater(); - qDebug() << "Got reply from http server"; - QJsonObject top = QJsonDocument::fromJson(m_data).object(); - float distance = (float ) top["water_level"].toArray()[0].toDouble(); - QByteArray data, data2; - qDebug() << "Updated on: " << top["updated_on"].toString(); - data = QByteArray::fromRawData((char *)&distance, sizeof(float)); - char *buff = new char[4]; - char *reader = (char *) &distance; - for(int i=0;i< sizeof(float) ;i++) { - qDebug() << i << "vs" << sizeof(float) - 1 - i; - buff[i] = reader[sizeof(float) - 1 - i]; - } - delete[] buff; - data2 = QByteArray::fromRawData(buff, sizeof(float)); - emit datagramReceived(data, data2); - emit lastUpdateddatagramReceived(top["updated_on"].toString().toUtf8()); -} - -void Worker::httpTimerTimeout() -{ - qDebug() << "Worker httpTimer Timeout"; - QNetworkRequest req; - req.setUrl(QUrl("https://unrealdragon.in/server/waterlevel")); - disconnect(manager, nullptr, nullptr, nullptr); - connect(manager, &QNetworkAccessManager::finished, this, &Worker::gotHttpReply); - manager->get(req); -} - -void Worker::stop() -{ - 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(socket_3) { - disconnect(socket_3, nullptr, nullptr, nullptr); - socket_3->close(); - delete socket_3; - } - if(httpTimer) { - disconnect(httpTimer, nullptr, nullptr, nullptr); - delete httpTimer; - } - if(manager) { - disconnect(manager, nullptr, nullptr, nullptr); - delete manager; - } - emit stopped(); -} - 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::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); + bool bind = socket->bind(QHostAddress::Any, 8180); #else - // Else use it as a receiver; bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176); - bool bind2 = true; #endif - if(!bind || !bind2 || !bind3) + if(!bind) { 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 - -} - -void Worker::changeMode(QString mode, QString url) -{ - qDebug() << "Worker changing mode: " << mode << url; - this->mode = mode; - this->url = url; - if(mode == "http" && QSysInfo::productType() != "android") { - httpTimer->setInterval(2000); - httpTimer->setSingleShot(false); - httpTimer->start(); - } - else if(mode == "udp") { - 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() -{ - if(socket_2->hasPendingDatagrams()) { - if(mode == "http") { - size_t data_size = socket_2->pendingDatagramSize(); - qDebug() << "Socket 2 has pending data: " << data_size; - char buff[data_size]; - socket_2->readDatagram(buff, data_size); - QByteArray data = QByteArray::fromRawData(buff, data_size); - emit lastUpdateddatagramReceived(data); - } - 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"; - } - } - } void Worker::socketDataReceived() { if(socket->hasPendingDatagrams()) { - if(mode == "udp" || (mode == "http" && QSysInfo::productType() == "android")) { - qDebug() << "Socket has pending data: " << socket->pendingDatagramSize(); - char buff[4] = {0x00, 0x00, 0x00, 0x00}; - socket->readDatagram(buff, 4); - auto data= QByteArray(buff,4); - char ch[4]; - for(int i=0;i<4;i++) { - ch[i] = buff[3-i]; - } - auto data2 = QByteArray(ch,4); - std::string correct_order = ""; - qDebug() << "Distance received: " << correct_order; - emit datagramReceived(data, data2); - } - else if(mode == "http") { - qDebug() << "Socket data received in http mode ignoring it."; - } - else { - qDebug() << "Mode is not set in socket."; + qDebug() << "Socket has pending data: " << socket->pendingDatagramSize(); + char buff[4] = {0x00, 0x00, 0x00, 0x00}; + socket->readDatagram(buff, 4); + auto data= QByteArray(buff,4); + char ch[4]; + for(int i=0;i<4;i++) { + ch[i] = buff[3-i]; } + auto data2 = QByteArray(ch,4); +#ifdef Q_OS_ANDROID + std::string correct_order = "0x" + data2.toHex().toStdString(); +#else + std::string correct_order = "0x" + data2.toHex().toStdString(); +#endif + qDebug() << "Distance received: " << correct_order; + uint32_t num; + sscanf(correct_order.c_str(), "%x", &num); + float dist = *((float*)&num); + qDebug() << "Float is: " << dist; + emit datagramReceived(dist); } } - -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 079dd35..0eefa61 100644 --- a/udplistener.h +++ b/udplistener.h @@ -8,45 +8,19 @@ #include #include #include -#include -#include -#include -#include -#include -#include #include "moving_container.h" -#ifdef Q_OS_ANDROID -#include "service.h" -#endif - - class Worker: public QObject { Q_OBJECT QUdpSocket *socket = nullptr; - QUdpSocket *socket_2 = nullptr; - QUdpSocket *socket_3 = nullptr; - QString mode = ""; - QString url = ""; - QNetworkAccessManager *manager = nullptr; - QTimer *httpTimer = nullptr; public: explicit Worker(QObject *parent = nullptr); signals: - void sendConfig(); - void stopped(); void socketError(QString error); - void lastUpdateddatagramReceived(QByteArray data); - void datagramReceived(QByteArray data, QByteArray data2); + void datagramReceived(float distance); public slots: - void gotHttpReply(QNetworkReply *rep); - void httpTimerTimeout(); - void stop(); void run(); - void changeMode(QString mode, QString url); private slots: - void socket3DataReceived(); - void socket2DataReceived(); void socketDataReceived(); }; @@ -55,51 +29,25 @@ 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; -#endif 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(); void showError(QString err); void distanceChanged(); void startWorker(); void connected(); 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(); - void resetAverages(); - void setFlipBits(bool flip_bits); - void setConfig(float distance, bool flip_bits, QString mode, QString url); + void setAlert(float distance); void socketError(QString err); void startService(); - void datagramRecieved(QByteArray data, QByteArray data2); + void datagramRecieved(float distance); private slots: void destroy(); void connectionTimerTimedOut(); - float convertToFloat(std::string hex); private: QUdpSocket *command_socket = nullptr; std::chrono::time_point prev_time_point; @@ -108,9 +56,6 @@ private: Worker *worker = nullptr; QThread *thread = nullptr; QTimer *timer = nullptr; - bool m_flip_bits; - QString m_mode; - QString m_url; }; #endif // UDPLISTENER_H