diff --git a/CMakeLists.txt b/CMakeLists.txt
index 45891ca..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
@@ -16,10 +16,22 @@ 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
@@ -29,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
@@ -45,6 +57,7 @@ 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 66f221e..e7844b9 100644
--- a/Main.qml
+++ b/Main.qml
@@ -6,19 +6,62 @@ import QtQuick.Controls.Material
Window {
width: 640
- height: 480
+ height: 1080
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: 18
+ 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: {
+ 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
@@ -145,8 +188,8 @@ Window {
border.color: root.accent_color
border.width: 1
}
- height: parent.height / 1.5
- width: parent.width / 1.5
+ height: Math.max(parent.height / 1.2, contentHeight)
+ width: parent.width / 1.1
anchors.centerIn: Overlay.overlay
@@ -176,24 +219,26 @@ 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.pixelSize: root.font_normal_text_size
+ font.pointSize: root.font_normal_text_size
}
TextField {
id: max_thres_input
text: config.max
anchors {
- left: max_thres_label.right
- leftMargin: 5
+ right: parent.right
+ rightMargin: 5
top: settings_label.bottom
topMargin: 20
}
+ width: parent.width / 2
color: root.accent_color
- font.pixelSize: root.font_normal_text_size
+ font.pointSize: root.font_normal_text_size
}
@@ -204,24 +249,26 @@ 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.pixelSize: root.font_normal_text_size
+ font.pointSize: root.font_normal_text_size
}
TextField {
id: min_thres_input
text: config.min
anchors {
- left: min_thres_label.right
- leftMargin: 5
+ right: parent.right
+ rightMargin: 5
top: max_thres_label.bottom
topMargin: 20
}
+ width: parent.width / 2
color: root.accent_color
- font.pixelSize: root.font_normal_text_size
+ font.pointSize: root.font_normal_text_size
}
@@ -232,24 +279,132 @@ 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.pixelSize: root.font_normal_text_size
+ font.pointSize: root.font_normal_text_size
}
TextField {
id: alert_thres_input
text: config.alert
anchors {
- left: alert_thres_label.right
- leftMargin: 5
+ right: parent.right
+ rightMargin: 5
top: min_thres_label.bottom
topMargin: 20
}
+ width: parent.width / 2
color: root.accent_color
- font.pixelSize: root.font_normal_text_size
+ 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
}
Text {
@@ -259,7 +414,9 @@ Window {
visible: settings_error_text === "" ? false : true
anchors {
topMargin: 20
- top: alert_thres_label.bottom
+ top: http_url_label.bottom
+ bottom: reset_averages.top
+ bottomMargin: 10
left: parent.left
horizontalCenter: parent.horizontalCenter
}
@@ -268,7 +425,52 @@ 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
@@ -278,7 +480,7 @@ Window {
horizontalCenter: parent.horizontalCenter
}
color: "#F0121212"
- width: parent.width / 2.5
+ width: parent.width / 1.3
height: 60
border {
width: 1
@@ -310,7 +512,10 @@ Window {
let max = parseFloat(min_thres_input.text);
let min = parseFloat(min_thres_input.text);
let alert = parseFloat(alert_thres_input.text);
- console.log("Parsed values:", max, min, alert);
+ 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);
if(max < min) {
settings_error_label.settings_error_text = "Max cannot be less than min";
return;
@@ -319,20 +524,19 @@ 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.setAlert(alert_effective_reading);
- config.saveConfig(max_thres_input.text, min_thres_input.text, alert_thres_input.text);
+ 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);
settings_popup.close();
}
onReleased: {
save_config.color = settings_rect_pressed_prev
}
}
+
}
}
}
@@ -354,7 +558,7 @@ Window {
id: tank_base
color: "#0FFFFFFF"
anchors.centerIn: parent
- width: parent.width / 2.5
+ width: parent.width / 2
height: parent.height / 2
border.color: "#F0121212"
border.width: 5
@@ -410,6 +614,20 @@ 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
@@ -417,7 +635,7 @@ Window {
text: "Error: " + error_string
visible: error_string === "" ? false : true;
anchors {
- top: info_percent_text.bottom
+ top: last_updated_text.bottom
topMargin: 10
horizontalCenter: parent.horizontalCenter
}
@@ -444,11 +662,40 @@ Window {
ConfigInterface {
id: config
- onConfigData: (max, min, alert) => {
- console.log("Got config data as: ", max, min, alert);
+ onConfigData: (max, min, alert, mode, url) => {
+ console.log("Got config data as: ", max, min, alert, mode, url);
+ }
+ onModeChanged: {
+ root.mode = mode;
}
}
+ 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
@@ -457,12 +704,29 @@ 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 >= 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);
@@ -478,7 +742,9 @@ 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;
@@ -507,23 +773,29 @@ 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.setAlert(alert_effective_reading);
+ listener.setConfig(alert_effective_reading, flip_bits_box.checked, root.mode, http_url_input.text);
}
distance_count += 1;
}
}
- 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
}
}
function dataBlink() {
- let old_color = status_indicator.color;
+ if(status_indicator_timer.running) {
+ status_indicator_timer.stop();
+ }
+ let old_color = "#F05F5F0F";
let new_color = "#F04FAF5F";
status_indicator.color = new_color;
status_indicator.old_color = old_color;
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/AndroidManifest.xml b/android/AndroidManifest.xml
index d31ff4c..ead6548 100644
--- a/android/AndroidManifest.xml
+++ b/android/AndroidManifest.xml
@@ -11,7 +11,7 @@
-
+
-
+ android:foregroundServiceType="connectedDevice"
+ 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 c868acc..cc765d0 100644
--- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java
+++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelService.java
@@ -19,39 +19,190 @@ 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", "Command thread initialising");
+ Log.d("UDP Service CommandThread", "Command thread initialising");
try {
- socket_cmd_recv = new DatagramSocket(8178, InetAddress.getByName("localhost"));
- socket_cmd_send = new DatagramSocket(8179);
-
- Log.d("UDP Service", "Created command thread sockets");
+ 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");
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);
- 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 CommandThread", "Received command packet " + str);
+ not_send_set = false;
if(str.startsWith("", "");
alert_thres_distance = Float.parseFloat(str);
- }
- Log.d("UDP Service ", "Received command packet " + str);
+ {
+ if(!checkBound())
+ {
+ setBound(true);
+ Log.d("UDP Service CommandThread", "Set Bound to True");
+ }
+ else {
+ Log.d("UDP Service CommandThread", "Set Bound is False");
+ }
+ }
+ }
+ 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) {
- Log.e("UDP Service", "Socket Error", e);
+ // showToast("IOException: " + e);
+ Log.e("UDP Service CommandThread", "IOException Error" + e);
}
}
@@ -61,13 +212,15 @@ 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", "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");
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");
@@ -84,17 +237,144 @@ public class WaterLevelService extends Service {
try {
multicastLock.acquire();
wakeLock.acquire();
- socket_recv = new DatagramSocket(8176);
- socket_send = new DatagramSocket(8177);
+ wifiLock.acquire();
+ socket_recv = new DatagramSocket(null);
+ socket_send = new DatagramSocket(null);
socket_recv.setBroadcast(true);
- Log.d("UDP Service", "Created Sockets");
+ 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;
while (!Thread.currentThread().isInterrupted()) {
- 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);
+ 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);
boolean duration_passed = false;
if(!started) {
duration_passed = true;
@@ -103,7 +383,7 @@ public class WaterLevelService extends Service {
else {
Instant currentTimestamp = Instant.now();
Duration duration = Duration.between(prevAlertTimestamp, currentTimestamp);
- if(duration.toMinutes() > 30) {
+ if(duration.toMinutes() > 60) {
duration_passed = true;
}
prevAlertTimestamp = currentTimestamp;
@@ -113,18 +393,33 @@ 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_HIGH);
+ .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;
+ }
}
- DatagramPacket send_packet = new DatagramPacket(buffer, buffer.length, InetAddress.getByName("localhost"), 8180);
+ 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);
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("127.0.0.1"), 8185);
+ socket_send.send(send_packet_2);
+ Log.d("UDP Service ClientThread", "Sent last updated packet");
+ }
+ }
}
catch (IOException e) {
- Log.e("UDP Service", "Socket Error", e);
+ // showToast("IOException: " + e);
+ Log.e("UDP Service ClientThread", "IOException Error" + e);
} finally {
+ if (wifiLock.isHeld())
+ wifiLock.release();
if (multicastLock.isHeld())
multicastLock.release();
if (wakeLock.isHeld())
@@ -136,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);
@@ -144,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
@@ -170,16 +465,47 @@ public class WaterLevelService extends Service {
Notification notification = createNotification();
startForeground(1, notification);
- Log.d("UDP Service", "Actually starting the thread");
+
+ 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");
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_STICKY;
+ return START_NOT_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";
@@ -202,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 98b114c..9dccce9 100644
--- a/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java
+++ b/android/src/org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter.java
@@ -5,15 +5,114 @@ 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");
- 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 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));
+ }
+ });
}
}
diff --git a/configinterface.cpp b/configinterface.cpp
index 563cf68..73ae20a 100644
--- a/configinterface.cpp
+++ b/configinterface.cpp
@@ -21,20 +21,30 @@ ConfigInterface::ConfigInterface(QObject *parent)
auto data = config.readAll();
config.close();
QJsonObject top = QJsonDocument::fromJson(data).object();
- QString max, min, alert;
- max = top["max_threshold"].toString();
- min = top["min_threshold"].toString();
- alert = top["alert_threshold"].toString();
+ 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");
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"] = "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";
QByteArray data = QJsonDocument(top).toJson();
bool ok = config.open(QFile::WriteOnly);
if(!ok) {
@@ -44,10 +54,17 @@ 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)
+void ConfigInterface::saveConfig(QString max, QString min, QString alert, bool flipbits, QString mode, QString url)
{
auto paths = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
qDebug() << "Got writable path as: " << paths;
@@ -58,9 +75,13 @@ void ConfigInterface::saveConfig(QString max, QString min, QString alert)
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);
@@ -106,3 +127,42 @@ 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 edf60ee..840cc2c 100644
--- a/configinterface.h
+++ b/configinterface.h
@@ -17,6 +17,9 @@ 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;
@@ -28,18 +31,33 @@ 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);
+ void configData(QString max, QString min, QString alert, bool flipbits, QString mode, QString url);
void alertChanged();
void maxChanged();
void minChanged();
+ void flipBitsChanged();
+ void modeChanged();
+ void urlChanged();
public slots:
- void saveConfig(QString max, QString min, QString alert);
+ void saveConfig(QString max, QString min, QString alert, bool flipbits, QString mode, QString url);
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 89a1cf0..966bce8 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,11 +21,16 @@ 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() {
@@ -36,6 +41,21 @@ 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 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 4978794..155df4e 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(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);
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,11 +21,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
@@ -39,26 +34,118 @@ void UDPListener::setDistance(double newDistance)
emit distanceChanged();
}
-void UDPListener::setAlert(float distance)
+void UDPListener::sendConfigOnSet()
{
+ 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 data;
- data.setDestination(QHostAddress::LocalHost, 8178);
+ 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);
+
QString s_data = "";
while(s_data.toUtf8().size() != 1024) {
s_data += "=";
}
- data.setData(s_data.toUtf8());
- command_socket->writeDatagram(data);
+ data1.setData(s_data.toUtf8());
+ command_socket->writeDatagram(data1);
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;
@@ -70,15 +157,34 @@ 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(float distance)
+void UDPListener::datagramRecieved(QByteArray data1, QByteArray data2)
{
- qDebug() << "Data received in main class: " << distance;
- averages.push_back(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);
prev_time_point = std::chrono::high_resolution_clock::now();
- emit distanceReceived(distance, QDateTime::currentDateTime().toString("dd/mm/yyyy hh:mm:ss.z ap"));
+ emit distanceReceived(data, QDateTime::currentDateTime().toString("dd/mm/yyyy hh:mm:ss.z ap"));
if(averages.size() >= 5) {
this->m_distance = averages.get_average();
emit distanceChanged();
@@ -91,11 +197,31 @@ void UDPListener::datagramRecieved(float distance)
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);
- thread->deleteLater();
+ 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);
timer->deleteLater();
+ QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
qDebug() << "Deinitialized UDP Listener";
}
@@ -111,51 +237,239 @@ 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
- bool bind = socket->bind(QHostAddress::Any, 8180);
+ // 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);
#else
+ // Else use it as a receiver;
bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176);
+ bool bind2 = true;
#endif
- if(!bind)
+ 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
+
+}
+
+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";
+ }
}
- connect(socket, &QUdpSocket::readyRead, this, &Worker::socketDataReceived);
}
void Worker::socketDataReceived()
{
if(socket->hasPendingDatagrams()) {
- 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];
+ 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.";
}
- 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 0eefa61..079dd35 100644
--- a/udplistener.h
+++ b/udplistener.h
@@ -8,19 +8,45 @@
#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 datagramReceived(float distance);
+ void lastUpdateddatagramReceived(QByteArray data);
+ void datagramReceived(QByteArray data, QByteArray data2);
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();
};
@@ -29,25 +55,51 @@ 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 setAlert(float distance);
+ 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 socketError(QString err);
void startService();
- void datagramRecieved(float distance);
+ void datagramRecieved(QByteArray data, QByteArray data2);
private slots:
void destroy();
void connectionTimerTimedOut();
+ float convertToFloat(std::string hex);
private:
QUdpSocket *command_socket = nullptr;
std::chrono::time_point prev_time_point;
@@ -56,6 +108,9 @@ private:
Worker *worker = nullptr;
QThread *thread = nullptr;
QTimer *timer = nullptr;
+ bool m_flip_bits;
+ QString m_mode;
+ QString m_url;
};
#endif // UDPLISTENER_H