532 lines
20 KiB
QML
532 lines
20 KiB
QML
|
|
import QtQuick
|
||
|
|
import waterLevelSensor
|
||
|
|
import QtQuick.Effects
|
||
|
|
import QtQuick.Controls
|
||
|
|
import QtQuick.Controls.Material
|
||
|
|
|
||
|
|
Window {
|
||
|
|
width: 640
|
||
|
|
height: 480
|
||
|
|
visible: true
|
||
|
|
title: qsTr("Hello World")
|
||
|
|
id: root
|
||
|
|
property color accent_color: "#F05FAF5F"
|
||
|
|
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 double distance: 0
|
||
|
|
property int distance_count: 0
|
||
|
|
// visibility: Window.FullScreen
|
||
|
|
Rectangle {
|
||
|
|
anchors.fill: parent
|
||
|
|
id: background
|
||
|
|
color: "#DF0F0F0F"
|
||
|
|
Rectangle {
|
||
|
|
id: top_bar
|
||
|
|
height: Math.min(root.height / 12, 100)
|
||
|
|
width: root.width
|
||
|
|
anchors {
|
||
|
|
top: parent.top
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
topMargin: 25
|
||
|
|
}
|
||
|
|
color: "#0A0F0F0F"
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: settings_rect
|
||
|
|
height: top_bar.height - 20
|
||
|
|
width: top_bar.width / 12
|
||
|
|
anchors {
|
||
|
|
top: parent.top
|
||
|
|
left: parent.left
|
||
|
|
bottomMargin: 5
|
||
|
|
topMargin: 5
|
||
|
|
leftMargin: 5
|
||
|
|
}
|
||
|
|
Behavior on color {
|
||
|
|
ColorAnimation {
|
||
|
|
duration: 200
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
color: "#0A0F0F0F"
|
||
|
|
border.color: "#AF121212"
|
||
|
|
border.width: 0.5
|
||
|
|
radius: 2
|
||
|
|
Text {
|
||
|
|
id: settings_text
|
||
|
|
text: "⚙"
|
||
|
|
anchors.centerIn: parent
|
||
|
|
color: root.accent_color
|
||
|
|
font.pointSize: root.font_enlarged_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
MouseArea {
|
||
|
|
id: settings_button_area
|
||
|
|
anchors.fill: parent
|
||
|
|
hoverEnabled: true
|
||
|
|
property color settings_rect_pressed_prev: "#00000000"
|
||
|
|
property color settings_rect_entered_prev: "#00000000"
|
||
|
|
onEntered: {
|
||
|
|
settings_rect_entered_prev = settings_rect.color
|
||
|
|
settings_rect.color = "#5A1F1F1F"
|
||
|
|
}
|
||
|
|
onExited: {
|
||
|
|
settings_rect.color = settings_rect_entered_prev
|
||
|
|
}
|
||
|
|
onPressed: {
|
||
|
|
settings_rect_pressed_prev = settings_rect.color
|
||
|
|
settings_rect.color = "#5A1F4F1F"
|
||
|
|
settings_popup.open()
|
||
|
|
}
|
||
|
|
onReleased: {
|
||
|
|
settings_rect.color = settings_rect_pressed_prev
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
property color old_color: "#0F0F0F0F"
|
||
|
|
id: status_indicator
|
||
|
|
width: 20
|
||
|
|
height: 20
|
||
|
|
radius: 20
|
||
|
|
anchors {
|
||
|
|
top: parent.top
|
||
|
|
right: parent.right
|
||
|
|
topMargin: 20
|
||
|
|
bottomMargin: 5
|
||
|
|
leftMargin: 5
|
||
|
|
rightMargin: 20
|
||
|
|
}
|
||
|
|
color: "#F0AF2F1F"
|
||
|
|
layer.enabled: true
|
||
|
|
layer.effect: MultiEffect {
|
||
|
|
shadowEnabled: true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Text {
|
||
|
|
id: status_label
|
||
|
|
anchors {
|
||
|
|
top: parent.top
|
||
|
|
topMargin: 20
|
||
|
|
right: status_indicator.left
|
||
|
|
rightMargin: 10
|
||
|
|
}
|
||
|
|
horizontalAlignment: Text.AlignRight
|
||
|
|
width: parent.width - status_indicator.width
|
||
|
|
height: parent.height
|
||
|
|
color: root.accent_color
|
||
|
|
font.pointSize: root.font_normal_text_size
|
||
|
|
text: "Disconnected"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: base
|
||
|
|
color: "#0A0F0F0F"
|
||
|
|
width: parent.width
|
||
|
|
height: parent.height - top_bar.height
|
||
|
|
anchors {
|
||
|
|
top: top_bar.bottom
|
||
|
|
bottom: parent.bottom
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
|
||
|
|
Popup {
|
||
|
|
id: settings_popup
|
||
|
|
background: Rectangle {
|
||
|
|
color: "#F0121212"
|
||
|
|
radius: 5
|
||
|
|
border.color: root.accent_color
|
||
|
|
border.width: 1
|
||
|
|
}
|
||
|
|
height: parent.height / 1.5
|
||
|
|
width: parent.width / 1.5
|
||
|
|
anchors.centerIn: Overlay.overlay
|
||
|
|
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: settings_base
|
||
|
|
anchors.fill: parent
|
||
|
|
color: "#F0121212"
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: settings_label
|
||
|
|
text: "Settings"
|
||
|
|
width: parent.width
|
||
|
|
anchors {
|
||
|
|
top: parent.top
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
horizontalAlignment: Text.AlignHCenter
|
||
|
|
color: root.accent_color
|
||
|
|
font.pointSize: root.font_enlarged_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: max_thres_label
|
||
|
|
text: "Max Threshold: "
|
||
|
|
anchors {
|
||
|
|
topMargin: 20
|
||
|
|
top: settings_label.bottom
|
||
|
|
left: parent.left
|
||
|
|
}
|
||
|
|
height: max_thres_input.height
|
||
|
|
verticalAlignment: Text.AlignVCenter
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
TextField {
|
||
|
|
id: max_thres_input
|
||
|
|
text: config.max
|
||
|
|
anchors {
|
||
|
|
left: max_thres_label.right
|
||
|
|
leftMargin: 5
|
||
|
|
top: settings_label.bottom
|
||
|
|
topMargin: 20
|
||
|
|
}
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: min_thres_label
|
||
|
|
text: "Min Threshold: "
|
||
|
|
anchors {
|
||
|
|
topMargin: 20
|
||
|
|
top: max_thres_label.bottom
|
||
|
|
left: parent.left
|
||
|
|
}
|
||
|
|
height: min_thres_input.height
|
||
|
|
verticalAlignment: Text.AlignVCenter
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
TextField {
|
||
|
|
id: min_thres_input
|
||
|
|
text: config.min
|
||
|
|
anchors {
|
||
|
|
left: min_thres_label.right
|
||
|
|
leftMargin: 5
|
||
|
|
top: max_thres_label.bottom
|
||
|
|
topMargin: 20
|
||
|
|
}
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: alert_thres_label
|
||
|
|
text: "Alert Percentage: "
|
||
|
|
anchors {
|
||
|
|
topMargin: 20
|
||
|
|
top: min_thres_label.bottom
|
||
|
|
left: parent.left
|
||
|
|
}
|
||
|
|
height: alert_thres_input.height
|
||
|
|
verticalAlignment: Text.AlignVCenter
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
TextField {
|
||
|
|
id: alert_thres_input
|
||
|
|
text: config.alert
|
||
|
|
anchors {
|
||
|
|
left: alert_thres_label.right
|
||
|
|
leftMargin: 5
|
||
|
|
top: min_thres_label.bottom
|
||
|
|
topMargin: 20
|
||
|
|
}
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: settings_error_label
|
||
|
|
property string settings_error_text: ""
|
||
|
|
text: "Error: " + settings_error_text
|
||
|
|
visible: settings_error_text === "" ? false : true
|
||
|
|
anchors {
|
||
|
|
topMargin: 20
|
||
|
|
top: alert_thres_label.bottom
|
||
|
|
left: parent.left
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
horizontalAlignment: Text.AlignHCenter
|
||
|
|
color: root.danger_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: save_config
|
||
|
|
anchors {
|
||
|
|
topMargin: 10
|
||
|
|
bottom: parent.bottom
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
color: "#F0121212"
|
||
|
|
width: parent.width / 2.5
|
||
|
|
height: 60
|
||
|
|
border {
|
||
|
|
width: 1
|
||
|
|
color: root.accent_color
|
||
|
|
}
|
||
|
|
radius: 3
|
||
|
|
|
||
|
|
Text {
|
||
|
|
anchors.centerIn: parent
|
||
|
|
text: "Save"
|
||
|
|
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 = save_config.color
|
||
|
|
save_config.color = "#5A1F1F1F"
|
||
|
|
}
|
||
|
|
onExited: {
|
||
|
|
save_config.color = settings_rect_entered_prev
|
||
|
|
}
|
||
|
|
onPressed: {
|
||
|
|
settings_rect_pressed_prev = save_config.color
|
||
|
|
save_config.color = "#5A1F4F1F"
|
||
|
|
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);
|
||
|
|
if(max < min) {
|
||
|
|
settings_error_label.settings_error_text = "Max cannot be less than min";
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if (alert > 100 || alert < 0) {
|
||
|
|
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);
|
||
|
|
settings_popup.close();
|
||
|
|
}
|
||
|
|
onReleased: {
|
||
|
|
save_config.color = settings_rect_pressed_prev
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: tank_lid
|
||
|
|
color: "#F0121212"
|
||
|
|
anchors {
|
||
|
|
bottom: tank_base.top
|
||
|
|
horizontalCenter: tank_base.horizontalCenter
|
||
|
|
bottomMargin: -5
|
||
|
|
}
|
||
|
|
radius: 10
|
||
|
|
width: parent.width / 4
|
||
|
|
height: parent.height / 15
|
||
|
|
}
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: tank_base
|
||
|
|
color: "#0FFFFFFF"
|
||
|
|
anchors.centerIn: parent
|
||
|
|
width: parent.width / 2.5
|
||
|
|
height: parent.height / 2
|
||
|
|
border.color: "#F0121212"
|
||
|
|
border.width: 5
|
||
|
|
radius: 4
|
||
|
|
}
|
||
|
|
|
||
|
|
Rectangle {
|
||
|
|
id: water_level
|
||
|
|
color: root.water_color
|
||
|
|
property double height_max: tank_base.height - 10
|
||
|
|
property double current_level: 0
|
||
|
|
property double percentage_filled: 0
|
||
|
|
height: water_level.current_level
|
||
|
|
width: tank_base.width - 10
|
||
|
|
anchors {
|
||
|
|
bottom: tank_base.bottom
|
||
|
|
horizontalCenter: tank_base.horizontalCenter
|
||
|
|
bottomMargin: 5
|
||
|
|
}
|
||
|
|
Behavior on color {
|
||
|
|
ColorAnimation {
|
||
|
|
duration: 200
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Behavior on height {
|
||
|
|
NumberAnimation {
|
||
|
|
duration: 200
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
Text {
|
||
|
|
id: info_text
|
||
|
|
text: "Current Reading: " + root.distance.toFixed(2) + " cm"
|
||
|
|
anchors {
|
||
|
|
top: tank_base.bottom
|
||
|
|
topMargin: 20
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
horizontalAlignment: Text.AlignHCenter
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: info_percent_text
|
||
|
|
text: "Percentage filled: " + water_level.percentage_filled.toFixed(2) + " %"
|
||
|
|
anchors {
|
||
|
|
top: info_text.bottom
|
||
|
|
topMargin: 10
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
horizontalAlignment: Text.AlignHCenter
|
||
|
|
color: root.accent_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
Text {
|
||
|
|
id: error_text
|
||
|
|
property string error_string: ""
|
||
|
|
text: "Error: " + error_string
|
||
|
|
visible: error_string === "" ? false : true;
|
||
|
|
anchors {
|
||
|
|
top: info_percent_text.bottom
|
||
|
|
topMargin: 10
|
||
|
|
horizontalCenter: parent.horizontalCenter
|
||
|
|
}
|
||
|
|
horizontalAlignment: Text.AlignHCenter
|
||
|
|
color: root.danger_color
|
||
|
|
font.pixelSize: root.font_normal_text_size
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
Timer {
|
||
|
|
id: status_indicator_timer
|
||
|
|
running: false
|
||
|
|
repeat: false
|
||
|
|
triggeredOnStart: false
|
||
|
|
interval: 300
|
||
|
|
onTriggered: {
|
||
|
|
status_indicator.color = status_indicator.old_color;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
ConfigInterface {
|
||
|
|
id: config
|
||
|
|
onConfigData: (max, min, alert) => {
|
||
|
|
console.log("Got config data as: ", max, min, alert);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
|
||
|
|
UDPListener {
|
||
|
|
id: listener
|
||
|
|
|
||
|
|
onShowError: (err) => {
|
||
|
|
error_text.error_string = err;
|
||
|
|
}
|
||
|
|
|
||
|
|
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) {
|
||
|
|
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);
|
||
|
|
let current_reading = Math.max(0, root.distance - current_min_reading);
|
||
|
|
let height_max_possible = water_level.height_max;
|
||
|
|
let percentage_filled = Math.max(0, ((effective_max_reading - current_reading) / effective_max_reading * 100))
|
||
|
|
let normalized_height = percentage_filled * height_max_possible / 100;
|
||
|
|
let alert = parseFloat(alert_thres_input.text);
|
||
|
|
if(percentage_filled <= alert) {
|
||
|
|
console.log("Water Level is lower than alert");
|
||
|
|
water_level.color = root.danger_color;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
water_level.color = root.water_color;
|
||
|
|
}
|
||
|
|
|
||
|
|
console.log("Current reading is: " + current_reading)
|
||
|
|
console.log("Normalized height is: " + normalized_height)
|
||
|
|
water_level.current_level = normalized_height;
|
||
|
|
water_level.percentage_filled = percentage_filled;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
onConnected: {
|
||
|
|
error_text.error_string = "";
|
||
|
|
console.log("Connected");
|
||
|
|
status_indicator.color = "#F05F5F0F"
|
||
|
|
status_label.text = "Waiting for more values";
|
||
|
|
|
||
|
|
}
|
||
|
|
onDisconnected: {
|
||
|
|
error_text.error_string = "Disconnected";
|
||
|
|
console.log("Disconnected");
|
||
|
|
status_indicator.color = "#F05F5F0F"
|
||
|
|
status_label.text = "Disconnected";
|
||
|
|
}
|
||
|
|
onDistanceReceived: (distance, time) => {
|
||
|
|
if(root.distance_count >= 1)
|
||
|
|
dataBlink();
|
||
|
|
if(root.distance_count == 0) {
|
||
|
|
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("Alert effective reading is: " + alert_effective_reading + " for value: " + alert);
|
||
|
|
listener.setAlert(alert_effective_reading);
|
||
|
|
}
|
||
|
|
|
||
|
|
distance_count += 1;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onClosing:(close)=> {
|
||
|
|
if(Qt.platform.os === "android"){
|
||
|
|
close.accepted = false
|
||
|
|
} else {
|
||
|
|
close.accepted = true
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function dataBlink() {
|
||
|
|
let old_color = status_indicator.color;
|
||
|
|
let new_color = "#F04FAF5F";
|
||
|
|
status_indicator.color = new_color;
|
||
|
|
status_indicator.old_color = old_color;
|
||
|
|
status_indicator_timer.start();
|
||
|
|
}
|
||
|
|
}
|