Merge pull request 'temp_main' (#2) from temp_main into main

Reviewed-on: https://unrealdragon.in/code/code/unrealdragon/WaterLevelSensor/pulls/2
Deleting branch temp main
This commit is contained in:
unrealdragon 2026-04-17 10:14:41 +00:00
commit a05c0d7302
11 changed files with 741 additions and 199 deletions

View file

@ -5,9 +5,9 @@ project(waterLevelSensor VERSION 0.1 LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTORCC 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 qt_add_executable(appwaterLevelSensor
main.cpp main.cpp
@ -41,8 +41,8 @@ set_property(TARGET service APPEND PROPERTY QT_ANDROID_PACKAGE_SOURCE_DIR
target_link_libraries(service target_link_libraries(service
PRIVATE PRIVATE
Qt::Core Qt::Core
Qt::CorePrivate
Qt::Network Qt::Network
Qt::CorePrivate
Qt::Quick Qt::Quick
) )
qt_add_qml_module(appwaterLevelSensor qt_add_qml_module(appwaterLevelSensor

View file

@ -19,11 +19,47 @@ Window {
property int font_normal_text_size: 16 property int font_normal_text_size: 16
property double distance: 0 property double distance: 0
property int distance_count: 0 property int distance_count: 0
property int distance_count_thres: 3
property string mode: "http" property string mode: "http"
// visibility: Window.FullScreen // visibility: Window.FullScreen
Component.onCompleted: { Component.onCompleted: {
if(Qt.platform.os !== "android") { loading_popup.open();
} }
Popup {
id: loading_popup
background: Rectangle {
color: "#B0121212"
radius: 5
border.color: root.accent_color
border.width: 1
}
height: parent.height / 10
width: parent.width / 1.1
anchors.centerIn: Overlay.overlay
closePolicy: Popup.NoAutoClose
BusyIndicator {
id: loading_indicator
running: true
anchors {
left: parent.left
verticalCenter: parent.verticalCenter
}
}
Text {
id: loading_text
text: "Loading..."
anchors {
leftMargin: 20
left: loading_indicator.right
verticalCenter: parent.verticalCenter
}
verticalAlignment: Text.AlignVCenter
color: root.accent_color
font.pointSize: root.font_enlarged_text_size
}
} }
Rectangle { Rectangle {
@ -634,11 +670,18 @@ Window {
} }
} }
Timer { Timer {
property int count: 0
id: init_timer id: init_timer
running: true running: true
repeat: false repeat: true
interval: 1000 interval: 1000
onTriggered: { onTriggered: {
if(count === 0) {
listener.startService();
count++;
return;
}
else {
console.log("Timer triggered saving config, hope everything is loaded by now"); 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 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 effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
@ -647,6 +690,9 @@ Window {
console.log("Setting initial configs: ", alert_effective_reading, config.flipBits , config.mode, config.url); 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); listener.setConfig(alert_effective_reading, config.flipBits , config.mode, config.url);
http_url_input.cursorPosition = 0; http_url_input.cursorPosition = 0;
init_timer.repeat = false;
init_timer.stop();
}
} }
} }
@ -658,9 +704,18 @@ Window {
error_text.error_string = err; error_text.error_string = err;
} }
onLastUpdatedChanged: (update) => { onLastUpdatedChanged: (update, warn) => {
console.log("QML Last Updated: ", update); console.log("QML Last Updated: ", update);
if(!warn)
{
last_updated_text.last_updated = update; 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: { onDistanceChanged: {
@ -668,7 +723,10 @@ Window {
root.distance = listener.distance; root.distance = listener.distance;
error_text.error_string = ""; error_text.error_string = "";
console.log("Distance count: ", root.distance_count); 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"; status_label.text = "Recieving values";
let current_min_reading = Math.max(0, (parseFloat(min_thres_input.text))); 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 effective_max_reading = Math.max(0, (parseFloat(max_thres_input.text)) - current_min_reading);
@ -685,7 +743,7 @@ Window {
water_level.color = root.water_color; water_level.color = root.water_color;
} }
if(root.distance_count > 1000) { if(root.distance_count > 1000) {
root.distance_count = 5; root.distance_count = root.distance_count_thres;
} }
console.log("Current reading is: " + current_reading) console.log("Current reading is: " + current_reading)
console.log("Normalized height is: " + normalized_height) console.log("Normalized height is: " + normalized_height)
@ -722,9 +780,12 @@ Window {
} }
} }
onClosing:(close)=> { onClosing: (close)=> {
if(Qt.platform.os === "android"){ if(Qt.platform.os === "android"){
close.accepted = false // close.accepted = false
// listener.moveAppToBackground();
// console.log("Moving to backgound");
close.accepted = true;
} else { } else {
close.accepted = true close.accepted = true
} }

View file

@ -6,3 +6,12 @@ On Android Creates a service from service.cpp and android/src/org/qtproject/exam
Changes in AndroidManifest.xml are important.<br> Changes in AndroidManifest.xml are important.<br>
On Desktop just waits for values from a UDP Broadcast on local network with port 8176 and shows those values.<br> On Desktop just waits for values from a UDP Broadcast on local network with port 8176 and shows those values.<br>
On Android the service does that, and creates an alert notification if the value in broadcast is less than alert threshold.<br> On Android the service does that, and creates an alert notification if the value in broadcast is less than alert threshold.<br>
<br>
Create a new socket in Worker that will instantly start listening.<br>
In the service launch the threads and immediately send client hello to the worker socket.<br>
On client hello send command data to the android service.<br>
On quit send server bye to android service.<br>
On server bye deinit the sockets that send data to cpp server.<br>
On next launch, the command and client threads can be reinitialized from the StartService java class.<br>

View file

@ -29,7 +29,9 @@
<service android:name=".WaterLevelService" <service android:name=".WaterLevelService"
android:foregroundServiceType="connectedDevice" android:foregroundServiceType="connectedDevice"
android:exported="true"> android:exported="true"
android:process=":water_service"
android:stopWithTask="false">
<!-- <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE" <!-- <property android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
android:value="Monitoring water levels via local network UDP packets for real-time sensor alerts." />--> android:value="Monitoring water levels via local network UDP packets for real-time sensor alerts." />-->
<meta-data android:name="android.app.lib_name" android:value="service"/> <meta-data android:name="android.app.lib_name" android:value="service"/>

View file

@ -25,42 +25,158 @@ import org.json.JSONException;
import org.json.JSONObject; import org.json.JSONObject;
import org.json.JSONArray; import org.json.JSONArray;
import android.widget.Toast; 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 { public class WaterLevelService extends Service {
private static float alert_thres_distance = 1000000; private static float alert_thres_distance = 1000000;
private boolean bits_flipped = false; private static boolean bits_flipped = false;
private String mode = ""; private static String mode = "";
private String url = ""; 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 { public class UDPCommandClient implements Runnable {
private DatagramSocket socket_cmd_recv; private DatagramSocket socket_cmd_recv;
private DatagramSocket socket_cmd_send; private DatagramSocket socket_cmd_send;
private boolean received_cmd_first = false;
@Override @Override
public void run() { public void run() {
Log.d("UDP Service", "Command thread initialising"); Log.d("UDP Service CommandThread", "Command thread initialising");
try { try {
socket_cmd_recv = new DatagramSocket(8178, InetAddress.getByName("localhost")); socket_cmd_recv = new DatagramSocket(null);
socket_cmd_send = new DatagramSocket(8179); socket_cmd_send = new DatagramSocket(null);
socket_cmd_recv.setSoTimeout(4000);
Log.d("UDP Service", "Created command thread sockets"); 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()) { 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("<SET>");
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]; byte[] buffer = new byte[1024];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
try {
socket_cmd_recv.receive(packet); socket_cmd_recv.receive(packet);
} catch(java.net.SocketTimeoutException e) {
continue;
}
String str = new String(buffer, StandardCharsets.UTF_8); String str = new String(buffer, StandardCharsets.UTF_8);
Log.d("UDP Service ", "Received command packet " + str); Log.d("UDP Service CommandThread", "Received command packet " + str);
not_send_set = false;
if(str.startsWith("<SET NAME=ALERT VALUE=")) { if(str.startsWith("<SET NAME=ALERT VALUE=")) {
str = str.replace("<SET NAME=ALERT VALUE=", ""); str = str.replace("<SET NAME=ALERT VALUE=", "");
str = str.replace("=", ""); str = str.replace("=", "");
str = str.replace(">", ""); str = str.replace(">", "");
alert_thres_distance = Float.parseFloat(str); 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");
}
}
} }
else if(str.startsWith("<SET NAME=BITS_FLIPPED VALUE=")) { else if(str.startsWith("<SET NAME=BITS_FLIPPED VALUE=")) {
str = str.replace("<SET NAME=BITS_FLIPPED VALUE=", ""); str = str.replace("<SET NAME=BITS_FLIPPED VALUE=", "");
str = str.replace("=", ""); str = str.replace("=", "");
str = str.replace(">", ""); str = str.replace(">", "");
Log.d("UDP Service ", "Flipping " + str); Log.d("UDP Service CommandThread", "Flipping " + str);
if(str.toLowerCase() == "false") { if(str.toLowerCase() == "false") {
bits_flipped = false; bits_flipped = false;
} }
@ -72,21 +188,21 @@ public class WaterLevelService extends Service {
str = str.replace("<SET NAME=MODE VALUE=", ""); str = str.replace("<SET NAME=MODE VALUE=", "");
str = str.replace("=", ""); str = str.replace("=", "");
str = str.replace(">", ""); str = str.replace(">", "");
Log.d("UDP Service ", "Mode " + str); Log.d("UDP Service CommandThread", "Mode " + str);
mode = str.toLowerCase(); mode = str.toLowerCase();
} }
else if(str.startsWith("<SET NAME=URL VALUE=")) { else if(str.startsWith("<SET NAME=URL VALUE=")) {
str = str.replace("<SET NAME=URL VALUE=", ""); str = str.replace("<SET NAME=URL VALUE=", "");
str = str.replace("=", ""); str = str.replace("=", "");
str = str.replace(">", ""); str = str.replace(">", "");
Log.d("UDP Service ", "Url " + str); Log.d("UDP Service CommandThread", "Url " + str);
url = str.toLowerCase(); url = str.toLowerCase();
} }
} }
} }
catch (IOException e) { catch (IOException e) {
// showToast("IOException: " + e); // showToast("IOException: " + e);
Log.e("UDP Service", "IOException Error" + e); Log.e("UDP Service CommandThread", "IOException Error" + e);
} }
} }
@ -96,11 +212,12 @@ public class WaterLevelService extends Service {
private DatagramSocket socket_recv; private DatagramSocket socket_recv;
private DatagramSocket socket_send; private DatagramSocket socket_send;
private Instant prevAlertTimestamp; private Instant prevAlertTimestamp;
private InetAddress address;
private boolean started = false; private boolean started = false;
@Override @Override
public void run() { public void run() {
Log.d("UDP Service", "Client Thread initialising"); Log.d("UDP Service ClientThread", "Client Thread initialising");
android.net.wifi.WifiManager wifi = (android.net.wifi.WifiManager) getSystemService(Context.WIFI_SERVICE); android.net.wifi.WifiManager wifi = (android.net.wifi.WifiManager) getSystemService(Context.WIFI_SERVICE);
android.net.wifi.WifiManager.MulticastLock multicastLock = wifi.createMulticastLock("water_level_lock"); 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.net.wifi.WifiManager.WifiLock wifiLock = wifi.createWifiLock(android.net.wifi.WifiManager.WIFI_MODE_FULL_HIGH_PERF, "WaterLevel:WifiLock");
@ -121,22 +238,76 @@ public class WaterLevelService extends Service {
multicastLock.acquire(); multicastLock.acquire();
wakeLock.acquire(); wakeLock.acquire();
wifiLock.acquire(); wifiLock.acquire();
socket_recv = new DatagramSocket(8176, InetAddress.getByName("0.0.0.0")); socket_recv = new DatagramSocket(null);
socket_send = new DatagramSocket(8177); socket_send = new DatagramSocket(null);
socket_recv.setBroadcast(true); socket_recv.setBroadcast(true);
socket_recv.setSoTimeout(4000); socket_recv.setSoTimeout(4000);
Log.d("UDP Service", "Created Sockets"); 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; int count = 0;
while (!Thread.currentThread().isInterrupted()) { while (!Thread.currentThread().isInterrupted()) {
count++; count++;
if(count == 3) { if(count == 3) {
Log.i("UDP Service", "Thread is running with mode: " + mode); Log.i("UDP Service ClientThread", "Thread is running with mode: " + mode);
count = 0; 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; byte[] buffer = null;
String updated = ""; String updated = "";
if(mode.equals("udp")) { if(mode.equals("udp")) {
try { try {
if(socket_recv.isClosed()) {
socket_recv.bind(new InetSocketAddress(8176));
}
buffer = new byte[4]; buffer = new byte[4];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length); DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket_recv.receive(packet); socket_recv.receive(packet);
@ -149,9 +320,10 @@ public class WaterLevelService extends Service {
try { try {
Thread.sleep(2000); Thread.sleep(2000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
continue; Log.d("UDP Service ClientThread", "Thread exiting due to interrupt.");
return;
} }
Log.d("UDP Service", "Http String Url: " + url); Log.d("UDP Service ClientThread", "Http String Url: " + url);
URL t_url = new URL(url); URL t_url = new URL(url);
HttpURLConnection con = (HttpURLConnection) t_url.openConnection(); HttpURLConnection con = (HttpURLConnection) t_url.openConnection();
con.setRequestMethod("GET"); con.setRequestMethod("GET");
@ -159,7 +331,7 @@ public class WaterLevelService extends Service {
con.setReadTimeout(5000); con.setReadTimeout(5000);
con.setInstanceFollowRedirects(true); con.setInstanceFollowRedirects(true);
int status = con.getResponseCode(); int status = con.getResponseCode();
Log.d("UDP Service", "Got status from http: " + status); Log.d("UDP Service ClientThread", "Got status from http: " + status);
if(status == 200) { if(status == 200) {
BufferedReader streamReader = new BufferedReader(new InputStreamReader(con.getInputStream())); BufferedReader streamReader = new BufferedReader(new InputStreamReader(con.getInputStream()));
String data = streamReader.readLine(); String data = streamReader.readLine();
@ -169,10 +341,10 @@ public class WaterLevelService extends Service {
float f = (float ) arr.getDouble(0); float f = (float ) arr.getDouble(0);
updated = obj.getString("updated_on"); updated = obj.getString("updated_on");
buffer = ByteBuffer.allocate(4).putFloat(f).array(); buffer = ByteBuffer.allocate(4).putFloat(f).array();
Log.d("UDP Service", "Got value from http: " + f); Log.d("UDP Service ClientThread", "Got value from http: " + f);
Log.d("UDP Service", "Last updated on: " + updated); Log.d("UDP Service ClientThread", "Last updated on: " + updated);
} catch (JSONException e) { } catch (JSONException e) {
Log.e("UDP Service", "Invalid Json"); Log.e("UDP Service ClientThread", "Invalid Json");
// showToast("Invalid Json" + e); // showToast("Invalid Json" + e);
continue; continue;
} }
@ -184,12 +356,13 @@ public class WaterLevelService extends Service {
} }
} }
else { else {
Log.d("UDP Service", "Invalid mode: " + mode); Log.d("UDP Service ClientThread", "Invalid mode: " + mode);
// showToast("Invalid mode: " + mode); // showToast("Invalid mode: " + mode);
try { try {
Thread.sleep(3000); Thread.sleep(3000);
} catch (InterruptedException e) { } catch (InterruptedException e) {
continue; Log.d("UDP Service ClientThread", "Thread exiting due to interrupt.");
return;
} }
continue; continue;
} }
@ -200,8 +373,8 @@ public class WaterLevelService extends Service {
else { else {
f = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN).getFloat(); f = ByteBuffer.wrap(buffer).order(ByteOrder.BIG_ENDIAN).getFloat();
} }
Log.d("UDP Service ", "Received packet: " + f); Log.d("UDP Service ClientThread", "Received packet: " + f);
Log.d("UDP Service ", "Threshold is: " + alert_thres_distance); Log.d("UDP Service ClientThread", "Threshold is: " + alert_thres_distance);
boolean duration_passed = false; boolean duration_passed = false;
if(!started) { if(!started) {
duration_passed = true; duration_passed = true;
@ -222,25 +395,28 @@ public class WaterLevelService extends Service {
.setContentText("Water Level is below alert threshold") .setContentText("Water Level is below alert threshold")
.setPriority(Notification.PRIORITY_LOW); .setPriority(Notification.PRIORITY_LOW);
manager.notify(2, builder.build()); manager.notify(2, builder.build());
Log.d("UDP Service", "Notification sent"); Log.d("UDP Service ClientThread", "Notification sent");
if(!started) { if(!started) {
started = true; 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); socket_send.send(send_packet);
Log.d("UDP Service ", "Sent packet"); Log.d("UDP Service ClientThread", "Sent packet");
if(mode.equals("http")) { if(mode.equals("http")) {
byte[] buff2 = updated.getBytes(StandardCharsets.UTF_8); byte[] buff2 = updated.getBytes(StandardCharsets.UTF_8);
DatagramPacket send_packet_2 = new DatagramPacket(buff2, buff2.length, InetAddress.getByName("localhost"), 8185); DatagramPacket send_packet_2 = new DatagramPacket(buff2, buff2.length, InetAddress.getByName("127.0.0.1"), 8185);
socket_send.send(send_packet_2); socket_send.send(send_packet_2);
Log.d("UDP Service ", "Sent last updated packet"); Log.d("UDP Service ClientThread", "Sent last updated packet");
} }
} }
} }
catch (IOException e) { catch (IOException e) {
// showToast("IOException: " + e); // showToast("IOException: " + e);
Log.e("UDP Service", "IOException Error" + e); Log.e("UDP Service ClientThread", "IOException Error" + e);
} finally { } finally {
if (wifiLock.isHeld()) if (wifiLock.isHeld())
wifiLock.release(); wifiLock.release();
@ -255,7 +431,7 @@ public class WaterLevelService extends Service {
} }
public void startWaterLevelService() { 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); Intent intent = new Intent(this, WaterLevelService.class);
@ -263,25 +439,25 @@ public class WaterLevelService extends Service {
} }
public WaterLevelService() { public WaterLevelService() {
super(); super();
Log.i("UDP Service", "Class is created"); Log.i("UDP Service MainThread", "Class is created");
} }
@Override @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 @Override
public void onCreate() { public void onCreate() {
super.onCreate(); super.onCreate();
Notification notification = createNotification(); Notification notification = createNotification();
startForeground(1, notification); startForeground(1, notification);
Log.i("UDP Service", "Creating Service"); Log.i("UDP Service MainThread", "Creating Service");
} }
@Override @Override
public void onDestroy() { public void onDestroy() {
super.onDestroy(); super.onDestroy();
Log.i("UDP Service", "Destroying Service"); Log.i("UDP Service MainThread", "Destroying Service");
} }
@Override @Override
@ -289,14 +465,40 @@ public class WaterLevelService extends Service {
Notification notification = createNotification(); Notification notification = createNotification();
startForeground(1, notification); 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); int ret = super.onStartCommand(intent, flags, startId);
(new Thread(new UDPClient())).start(); // (new Thread(new UDPClient())).start();
(new Thread(new UDPCommandClient())).start(); // (new Thread(new UDPCommandClient())).start();
return START_STICKY; return START_NOT_STICKY;
} }
public void showToast(String str) { public void showToast(String str) {
@ -326,7 +528,7 @@ public class WaterLevelService extends Service {
} }
public static void startQtAndroidService(QtActivity activity) { 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); Intent intent = new Intent(activity, WaterLevelService.class);
activity.startForegroundService(intent); activity.startForegroundService(intent);

View file

@ -5,15 +5,114 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.util.Log; import android.util.Log;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningServiceInfo;
import org.qtproject.qt.android.bindings.QtActivity; 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 { 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) { 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));
}
});
} }
} }

View file

@ -23,9 +23,9 @@ ConfigInterface::ConfigInterface(QObject *parent)
QJsonObject top = QJsonDocument::fromJson(data).object(); QJsonObject top = QJsonDocument::fromJson(data).object();
QString max, min, alert, mode, url; QString max, min, alert, mode, url;
bool flipbits; bool flipbits;
max = top["max_threshold"].toString("100"); max = top["max_threshold"].toString("110");
min = top["min_threshold"].toString("20"); min = top["min_threshold"].toString("10");
alert = top["alert_threshold"].toString("60"); alert = top["alert_threshold"].toString("30");
flipbits = top["flip_bits"].toBool(true); flipbits = top["flip_bits"].toBool(true);
mode = top["mode"].toString("http"); mode = top["mode"].toString("http");
url = top["url"].toString("https://unrealdragon.in/server/waterlevel"); url = top["url"].toString("https://unrealdragon.in/server/waterlevel");
@ -39,9 +39,9 @@ ConfigInterface::ConfigInterface(QObject *parent)
else { else {
qDebug() << "File does not exist creating one..."; qDebug() << "File does not exist creating one...";
QJsonObject top; QJsonObject top;
top["max_threshold"] = "125"; top["max_threshold"] = "110";
top["min_threshold"] = "25"; top["min_threshold"] = "10";
top["alert_threshold"] = "100"; top["alert_threshold"] = "30";
top["flip_bits"] = true; top["flip_bits"] = true;
top["mode"] = "http"; top["mode"] = "http";
top["url"] = "https://unrealdragon.in/server/waterlevel"; top["url"] = "https://unrealdragon.in/server/waterlevel";
@ -54,9 +54,9 @@ ConfigInterface::ConfigInterface(QObject *parent)
config.write(data); config.write(data);
config.flush(); config.flush();
config.close(); config.close();
setMax("125"); setMax("110");
setMin("25"); setMin("10");
setAlert("100"); setAlert("30");
setFlipBits(true); setFlipBits(true);
setMode("http"); setMode("http");
setUrl("https://unrealdragon.in/server/waterlevel"); setUrl("https://unrealdragon.in/server/waterlevel");

View file

@ -11,7 +11,6 @@
Q_DECLARE_JNI_CLASS(ComponentName, "android/content/ComponentName") Q_DECLARE_JNI_CLASS(ComponentName, "android/content/ComponentName")
Q_DECLARE_JNI_CLASS(WaterLevelService, "org/qtproject/example/appwaterLevelSensor/WaterLevelService") Q_DECLARE_JNI_CLASS(WaterLevelService, "org/qtproject/example/appwaterLevelSensor/WaterLevelService")
static volatile float level = -1; static volatile float level = -1;
static void handleWaterLevelUpdate(JNIEnv *env, jobject /*obj*/, jbyteArray data) { 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); env->ReleaseByteArrayElements(data, bytes, JNI_ABORT);
} }
extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
if (vm->GetEnv(reinterpret_cast<void**>(&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) service::service(QObject *parent)
: QObject{parent} : QObject{parent}
{ {
if(QSysInfo::productType() == "android") { if(QSysInfo::productType() == "android") {
qDebug() << "Running android service"; qDebug() << "Running android service";
// QJniEnvironment env;
// // if (env->ExceptionCheck()) env->ExceptionClear();
// // static bool nativesRegistered = false;
using namespace QtJniTypes; // // if(!nativesRegistered) {
using namespace QNativeInterface; // // // 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<QAndroidApplication>(); // auto *androidApp = qGuiApp->nativeInterface<QAndroidApplication>();
// Context context = androidApp->context(); // // Context context = androidApp->context();
QJniObject context = QNativeInterface::QAndroidApplication::context(); // QJniObject context = QNativeInterface::QAndroidApplication::context();
// auto serviceClass = env.findClass(Traits<WaterLevelService>::className());
QJniEnvironment env; // if (!serviceClass) {
auto serviceClass = env.findClass(Traits<WaterLevelService>::className()); // qDebug() << "FAILED to find WaterLevelService class!";
if (!serviceClass) { // env->ExceptionDescribe(); // VERY important
qDebug() << "FAILED to find WaterLevelService class!"; // env->ExceptionClear();
env->ExceptionDescribe(); // VERY important // return;
env->ExceptionClear(); // }
return; // if (context.isValid()) {
// QJniObject::callStaticMethod<
// void>(
// "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter",
// "startService",
// "(Landroid/content/Context;)V",
// context.object<jobject>());
// }
} }
}
jclass javaClass = env.findClass("org/qtproject/example/appwaterLevelSensor/WaterLevelReceiver"); void service::moveAppToBackground()
env->RegisterNatives(javaClass, methods, 1); {
QJniObject activity = QNativeInterface::QAndroidApplication::context();
if (activity.isValid()) {
qDebug() << "Moved app to background";
activity.callMethod<jboolean>("moveTaskToBack", "(Z)Z", true);
}
qDebug() << "Closing";
}
void service::begin() {
// Only call this once the UI is visible
QJniObject context = QNativeInterface::QAndroidApplication::context();
QJniObject::callStaticMethod<void>( QJniObject::callStaticMethod<void>(
"org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter", "org/qtproject/example/appwaterLevelSensor/WaterLevelServiceStarter",
"startService", "startService",
"(Landroid/content/Context;)V", "(Landroid/content/Context;)V",
context.object<jobject>()); // pass the QtActivity instance ); context.object<jobject>()
);
// QJniObject activity = QNativeInterface::QAndroidApplication::context();
// activity.callMethod<void>("startWaterLevelService", "()V");
// Intent serviceIntent(activity, serviceClass);
// activity.callMethod<WaterLevelService>("startWaterLevelService");
// activity.callMethod<()
// QJniObject::callStaticMethod<void>(
// "org/qtproject/example/appwaterLevelSensor/WaterLevelService",
// "startQtAndroidService",
// "(Landroid/content/Context;)V",
// activity.object<jobject>()
// );
// activity.callMethod<void>("startWaterLevelService", "()V"); // Intent serviceIntent(context, serviceClass);
// context.callMethod<ComponentName>("startService", serviceIntent);
// context.callMethod<ComponentName>(
// "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<jstring>()
// );
// 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<void>("startQtAndroidService", context);
}
} }

View file

@ -8,6 +8,8 @@ class service : public QObject
Q_OBJECT Q_OBJECT
public: public:
explicit service(QObject *parent = nullptr); explicit service(QObject *parent = nullptr);
void moveAppToBackground();
void begin();
signals: signals:
}; };

View file

@ -1,19 +1,17 @@
#include "udplistener.h" #include "udplistener.h"
#include <QGuiApplication> #include <QGuiApplication>
#ifdef Q_OS_ANDROID
#include "service.h"
#endif
UDPListener::UDPListener(QObject *parent) UDPListener::UDPListener(QObject *parent)
: QObject{parent} : QObject{parent}
{ {
worker = new Worker(this); worker = new Worker();
thread = new QThread(this); thread = new QThread();
worker->moveToThread(thread); worker->moveToThread(thread);
connect(worker, &Worker::lastUpdateddatagramReceived, this, &UDPListener::lastUpdateddatagramReceived); connect(worker, &Worker::lastUpdateddatagramReceived, this, &UDPListener::lastUpdateddatagramReceived);
connect(worker, &Worker::datagramReceived, this, &UDPListener::datagramRecieved); connect(worker, &Worker::datagramReceived, this, &UDPListener::datagramRecieved);
connect(worker, &Worker::socketError, this, &UDPListener::socketError); connect(worker, &Worker::socketError, this, &UDPListener::socketError);
connect(worker, &Worker::sendConfig, this, &UDPListener::sendConfigOnSet);
connect(this, &UDPListener::startWorker, worker, &Worker::run); connect(this, &UDPListener::startWorker, worker, &Worker::run);
connect(this, &UDPListener::changeWorkerMode, worker, &Worker::changeMode); connect(this, &UDPListener::changeWorkerMode, worker, &Worker::changeMode);
thread->start(); thread->start();
@ -23,11 +21,6 @@ UDPListener::UDPListener(QObject *parent)
timer->setSingleShot(false); timer->setSingleShot(false);
connect(timer, &QTimer::timeout, this, &UDPListener::connectionTimerTimedOut); connect(timer, &QTimer::timeout, this, &UDPListener::connectionTimerTimedOut);
connect(qApp, &QCoreApplication::aboutToQuit, this, &UDPListener::destroy); 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 double UDPListener::distance() const
@ -41,34 +34,15 @@ void UDPListener::setDistance(double newDistance)
emit distanceChanged(); emit distanceChanged();
} }
void UDPListener::lastUpdateddatagramReceived(QByteArray data) void UDPListener::sendConfigOnSet()
{ {
QString s_data = QString::fromUtf8(data); bool flip_bits = m_flip_bits;
qDebug() << "Main Class last updated on: " << s_data; QString url = m_url;
emit lastUpdatedChanged(s_data); QString mode = m_mode;
} float distance = m_distance;
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);
if(!command_socket) { if(!command_socket) {
command_socket = new QUdpSocket(this); command_socket = new QUdpSocket(this);
bool bind = command_socket->bind(8181); bool bind = command_socket->bind(QHostAddress::Any, 0);
if(bind) { if(bind) {
QNetworkDatagram data1, data2, data3, data4; QNetworkDatagram data1, data2, data3, data4;
data1.setDestination(QHostAddress::LocalHost, 8178); data1.setDestination(QHostAddress::LocalHost, 8178);
@ -112,9 +86,63 @@ void UDPListener::setConfig(float distance, bool flip_bits, QString mode, QStrin
command_socket->deleteLater(); command_socket->deleteLater();
command_socket = nullptr; command_socket = nullptr;
qDebug() << "Sent all Command Statements.";
emit changeWorkerMode(mode, url); 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(); averages.clear();
} }
@ -129,6 +157,12 @@ void UDPListener::socketError(QString err)
void UDPListener::startService() void UDPListener::startService()
{ {
#ifdef Q_OS_ANDROID
qDebug() << "Running on Android";
service = new class service();
qDebug() << "Ran service";
service->begin();
#endif
} }
@ -163,25 +197,31 @@ void UDPListener::datagramRecieved(QByteArray data1, QByteArray data2)
void UDPListener::destroy() void UDPListener::destroy()
{ {
qDebug() << "Destroying UDP Listener"; qDebug() << "Destroying UDP Listener";
#ifdef Q_OS_ANDROID
delete service;
#endif
disconnect(timer, nullptr, nullptr, nullptr);
timer->stop(); timer->stop();
QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
connect(this, &UDPListener::stopWorker, worker, &Worker::stop); connect(this, &UDPListener::stopWorker, worker, &Worker::stop);
connect(worker, &Worker::stopped, this, &UDPListener::continueDestruction); connect(worker, &Worker::stopped, this, &UDPListener::continueDestruction);
emit stopWorker(); emit stopWorker();
while(!destroyed) { while(!destroyed) {
QCoreApplication::processEvents(); QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
} }
thread->exit(0); thread->exit(0);
QCoreApplication::processEvents(); QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
QCoreApplication::processEvents();
worker->deleteLater(); worker->deleteLater();
QCoreApplication::processEvents(); QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
QCoreApplication::processEvents(); while(thread->isRunning()) {
qDebug() << "The thread is still running.";
QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
}
thread->deleteLater(); thread->deleteLater();
QCoreApplication::processEvents(); QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
QCoreApplication::processEvents();
timer->deleteLater(); timer->deleteLater();
QCoreApplication::processEvents(); QCoreApplication::processEvents(QEventLoop::AllEvents, 2000);
QCoreApplication::processEvents();
qDebug() << "Deinitialized UDP Listener"; qDebug() << "Deinitialized UDP Listener";
} }
@ -234,6 +274,7 @@ void Worker::gotHttpReply(QNetworkReply *rep)
void Worker::httpTimerTimeout() void Worker::httpTimerTimeout()
{ {
qDebug() << "Worker httpTimer Timeout";
QNetworkRequest req; QNetworkRequest req;
req.setUrl(QUrl("https://unrealdragon.in/server/waterlevel")); req.setUrl(QUrl("https://unrealdragon.in/server/waterlevel"));
disconnect(manager, nullptr, nullptr, nullptr); disconnect(manager, nullptr, nullptr, nullptr);
@ -243,8 +284,21 @@ void Worker::httpTimerTimeout()
void Worker::stop() 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); disconnect(socket, nullptr, nullptr, nullptr);
socket->close();
delete socket; delete socket;
}
if(socket_3) {
disconnect(socket_3, nullptr, nullptr, nullptr);
socket_3->close();
delete socket_3;
}
if(httpTimer) { if(httpTimer) {
disconnect(httpTimer, nullptr, nullptr, nullptr); disconnect(httpTimer, nullptr, nullptr, nullptr);
delete httpTimer; delete httpTimer;
@ -261,26 +315,36 @@ void Worker::run()
using namespace std::chrono_literals; using namespace std::chrono_literals;
socket = new QUdpSocket(this); socket = new QUdpSocket(this);
socket_2 = new QUdpSocket(this); socket_2 = new QUdpSocket(this);
socket_3 = new QUdpSocket(this);
httpTimer = new QTimer(this); httpTimer = new QTimer(this);
manager = new QNetworkAccessManager(this); manager = new QNetworkAccessManager(this);
#ifdef Q_OS_ANDROID #ifdef Q_OS_ANDROID
// If it is android use it as a command socket; // If it is android use it as a command socket;
bool bind = socket->bind(QHostAddress::Any, 8180); bool bind = socket->bind(QHostAddress::LocalHost, 8180, QUdpSocket::ReuseAddressHint | QUdpSocket::ShareAddress);
bool bind2 = socket_2->bind(QHostAddress::Any, 8185); 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
// Else use it as a receiver; // Else use it as a receiver;
bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176); bool bind = socket->bind(QHostAddress("192.168.1.255"), 8176);
bool bind2 = true; bool bind2 = true;
#endif #endif
if(!bind || !bind2) if(!bind || !bind2 || !bind3)
{ {
qDebug() << "UI Worker socket bind failed" << bind << socket->errorString(); 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->errorString());
emit socketError(socket_2->errorString()); emit socketError(socket_2->errorString());
emit socketError(socket_3->errorString());
qApp->exit(-1);
} }
connect(httpTimer, &QTimer::timeout, this, &Worker::httpTimerTimeout); connect(httpTimer, &QTimer::timeout, this, &Worker::httpTimerTimeout);
connect(socket, &QUdpSocket::readyRead, this, &Worker::socketDataReceived); connect(socket, &QUdpSocket::readyRead, this, &Worker::socketDataReceived);
connect(socket_2, &QUdpSocket::readyRead, this, &Worker::socket2DataReceived); 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
} }
@ -298,6 +362,43 @@ void Worker::changeMode(QString mode, QString url)
httpTimer->stop(); httpTimer->stop();
socketDataReceived(); 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) == "<SET>") {
qDebug() << "Data Received from Service Threads.";
emit sendConfig();
}
}
}
} }
void Worker::socket2DataReceived() void Worker::socket2DataReceived()
@ -314,6 +415,9 @@ void Worker::socket2DataReceived()
else if(mode == "udp") { else if(mode == "udp") {
qDebug() << "Last Updated on Socket data received in udp mode ignoring it."; qDebug() << "Last Updated on Socket data received in udp mode ignoring it.";
} }
else {
qDebug() << "Mode is not set in socket 2";
}
} }
} }
@ -338,5 +442,34 @@ void Worker::socketDataReceived()
else if(mode == "http") { else if(mode == "http") {
qDebug() << "Socket data received in http mode ignoring it."; qDebug() << "Socket data received in http mode ignoring it.";
} }
else {
qDebug() << "Mode is not set in socket.";
}
} }
} }
QString UDPListener::mode() const
{
return m_mode;
}
void UDPListener::setMode(const QString &newMode)
{
if (m_mode == newMode)
return;
m_mode = newMode;
emit modeChanged();
}
QString UDPListener::url() const
{
return m_url;
}
void UDPListener::setUrl(const QString &newUrl)
{
if (m_url == newUrl)
return;
m_url = newUrl;
emit urlChanged();
}

View file

@ -16,10 +16,16 @@
#include <QJsonObject> #include <QJsonObject>
#include "moving_container.h" #include "moving_container.h"
#ifdef Q_OS_ANDROID
#include "service.h"
#endif
class Worker: public QObject { class Worker: public QObject {
Q_OBJECT Q_OBJECT
QUdpSocket *socket = nullptr; QUdpSocket *socket = nullptr;
QUdpSocket *socket_2 = nullptr; QUdpSocket *socket_2 = nullptr;
QUdpSocket *socket_3 = nullptr;
QString mode = ""; QString mode = "";
QString url = ""; QString url = "";
QNetworkAccessManager *manager = nullptr; QNetworkAccessManager *manager = nullptr;
@ -27,6 +33,7 @@ class Worker: public QObject {
public: public:
explicit Worker(QObject *parent = nullptr); explicit Worker(QObject *parent = nullptr);
signals: signals:
void sendConfig();
void stopped(); void stopped();
void socketError(QString error); void socketError(QString error);
void lastUpdateddatagramReceived(QByteArray data); void lastUpdateddatagramReceived(QByteArray data);
@ -38,6 +45,7 @@ public slots:
void run(); void run();
void changeMode(QString mode, QString url); void changeMode(QString mode, QString url);
private slots: private slots:
void socket3DataReceived();
void socket2DataReceived(); void socket2DataReceived();
void socketDataReceived(); void socketDataReceived();
}; };
@ -47,13 +55,24 @@ class UDPListener : public QObject
Q_OBJECT Q_OBJECT
QML_ELEMENT QML_ELEMENT
Q_PROPERTY(double distance READ distance WRITE setDistance NOTIFY distanceChanged FINAL) 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; bool destroyed = false;
#ifdef Q_OS_ANDROID
service *service = nullptr;
#endif
public: public:
explicit UDPListener(QObject *parent = nullptr); explicit UDPListener(QObject *parent = nullptr);
double distance() const; double distance() const;
void setDistance(double newDistance); void setDistance(double newDistance);
QString mode() const;
void setMode(const QString &newMode);
QString url() const;
void setUrl(const QString &newUrl);
signals: signals:
void lastUpdatedChanged(QString last_updated); void lastUpdatedChanged(QString last_updated, bool warn);
void stopWorker(); void stopWorker();
void showError(QString err); void showError(QString err);
void distanceChanged(); void distanceChanged();
@ -62,7 +81,13 @@ signals:
void disconnected(); void disconnected();
void distanceReceived(double dist, QString timestamp); void distanceReceived(double dist, QString timestamp);
void changeWorkerMode(QString mode, QString url); void changeWorkerMode(QString mode, QString url);
void modeChanged();
void initLoadFinished();
void urlChanged();
public slots: public slots:
void sendConfigOnSet();
void moveAppToBackground();
void lastUpdateddatagramReceived(QByteArray data); void lastUpdateddatagramReceived(QByteArray data);
void continueDestruction(); void continueDestruction();
void resetAverages(); void resetAverages();
@ -84,6 +109,8 @@ private:
QThread *thread = nullptr; QThread *thread = nullptr;
QTimer *timer = nullptr; QTimer *timer = nullptr;
bool m_flip_bits; bool m_flip_bits;
QString m_mode;
QString m_url;
}; };
#endif // UDPLISTENER_H #endif // UDPLISTENER_H