Http Version - Initial Commit
This commit is contained in:
commit
026ea97284
12 changed files with 978 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
build/*
|
||||||
|
.qtcreator/*
|
||||||
46
CMakeLists.txt
Normal file
46
CMakeLists.txt
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
cmake_minimum_required(VERSION 3.16)
|
||||||
|
|
||||||
|
project(WaterLevelRewrite VERSION 0.1 LANGUAGES CXX)
|
||||||
|
|
||||||
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
|
find_package(Qt6 REQUIRED COMPONENTS Quick)
|
||||||
|
|
||||||
|
qt_standard_project_setup(REQUIRES 6.10)
|
||||||
|
|
||||||
|
qt_add_executable(appWaterLevelRewrite
|
||||||
|
main.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
qt_add_qml_module(appWaterLevelRewrite
|
||||||
|
URI WaterLevelRewrite
|
||||||
|
QML_FILES
|
||||||
|
Main.qml
|
||||||
|
QML_FILES WaterTank.qml
|
||||||
|
QML_FILES SettingsBarIcon.qml
|
||||||
|
QML_FILES SettingsPopup.qml
|
||||||
|
SOURCES configinterface.h configinterface.cpp
|
||||||
|
QML_FILES ConnectButton.qml
|
||||||
|
SOURCES requestworker.h requestworker.cpp
|
||||||
|
)
|
||||||
|
|
||||||
|
# Qt for iOS sets MACOSX_BUNDLE_GUI_IDENTIFIER automatically since Qt 6.1.
|
||||||
|
# If you are developing for iOS or macOS you should consider setting an
|
||||||
|
# explicit, fixed bundle identifier manually though.
|
||||||
|
set_target_properties(appWaterLevelRewrite PROPERTIES
|
||||||
|
# MACOSX_BUNDLE_GUI_IDENTIFIER com.example.appWaterLevelRewrite
|
||||||
|
MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION}
|
||||||
|
MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
|
||||||
|
MACOSX_BUNDLE TRUE
|
||||||
|
WIN32_EXECUTABLE TRUE
|
||||||
|
)
|
||||||
|
|
||||||
|
target_link_libraries(appWaterLevelRewrite
|
||||||
|
PRIVATE Qt6::Quick
|
||||||
|
)
|
||||||
|
|
||||||
|
install(TARGETS appWaterLevelRewrite
|
||||||
|
BUNDLE DESTINATION .
|
||||||
|
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
|
||||||
|
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
|
)
|
||||||
55
ConnectButton.qml
Normal file
55
ConnectButton.qml
Normal file
|
|
@ -0,0 +1,55 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: root
|
||||||
|
property color accent_color: "#00000000"
|
||||||
|
property color font_color: "#00000000"
|
||||||
|
property int font_enlarged_text_size: 20
|
||||||
|
property int font_normal_text_size: 16
|
||||||
|
property string text: "Connect"
|
||||||
|
signal connect;
|
||||||
|
layer.enabled: true
|
||||||
|
layer.effect: MultiEffect {
|
||||||
|
shadowEnabled: true
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
font.pointSize: root.font_enlarged_text_size
|
||||||
|
color: root.font_color
|
||||||
|
text: root.text
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
radius: 5
|
||||||
|
border {
|
||||||
|
color: root.accent_color
|
||||||
|
width: 0.5
|
||||||
|
}
|
||||||
|
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 = root.color
|
||||||
|
root.color = "#5A1F1F1F"
|
||||||
|
}
|
||||||
|
onExited: {
|
||||||
|
root.color = settings_rect_entered_prev
|
||||||
|
}
|
||||||
|
onPressed: {
|
||||||
|
settings_rect_pressed_prev = root.color
|
||||||
|
root.color = "#5A1F4F1F"
|
||||||
|
root.connect();
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
root.color = settings_rect_pressed_prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
156
Main.qml
Normal file
156
Main.qml
Normal file
|
|
@ -0,0 +1,156 @@
|
||||||
|
import QtQuick
|
||||||
|
import WaterLevelRewrite
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
Window {
|
||||||
|
width: 640
|
||||||
|
height: 1080
|
||||||
|
visible: true
|
||||||
|
title: qsTr("Water Level Rewrite")
|
||||||
|
id: root
|
||||||
|
property color accent_color: "#F05FAF5F"
|
||||||
|
property color dark_accent_color: "#FF5F7F5F"
|
||||||
|
property color danger_color: "#F0AF2F1F"
|
||||||
|
property color water_color: "#F00F7FAF"
|
||||||
|
property color background_color: "#1F1F1F"
|
||||||
|
Material.accent: root.accent_color
|
||||||
|
property int font_enlarged_text_size: 20
|
||||||
|
property int font_normal_text_size: 16
|
||||||
|
property int distance_count_thres: 3
|
||||||
|
property string mode: "http"
|
||||||
|
property string status: "Initializing"
|
||||||
|
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
anchors.fill: parent
|
||||||
|
color: root.background_color
|
||||||
|
|
||||||
|
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"
|
||||||
|
SettingsBarIcon {
|
||||||
|
height: top_bar.height - 20
|
||||||
|
width: top_bar.width / 12
|
||||||
|
anchors {
|
||||||
|
top: parent.top
|
||||||
|
left: parent.left
|
||||||
|
bottomMargin: 5
|
||||||
|
topMargin: 5
|
||||||
|
leftMargin: 5
|
||||||
|
}
|
||||||
|
color: "#0A0F0F0F"
|
||||||
|
radius: 100
|
||||||
|
accent_color: root.accent_color
|
||||||
|
onOpen: {
|
||||||
|
settings_popup.open()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SettingsPopup {
|
||||||
|
id: settings_popup
|
||||||
|
accent_color: root.accent_color
|
||||||
|
font_enlarged_text_size: 20
|
||||||
|
function setConfig(new_config: var) {
|
||||||
|
settings_popup.config.max = new_config.max
|
||||||
|
settings_popup.config.min = new_config.min
|
||||||
|
settings_popup.config.alert = new_config.alert
|
||||||
|
settings_popup.config.url = new_config.url
|
||||||
|
}
|
||||||
|
onSaveConfig: (config) => {
|
||||||
|
console.log("Saving config: ", config)
|
||||||
|
config_interface.saveConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterTank {
|
||||||
|
id: tank
|
||||||
|
anchors {
|
||||||
|
top: top_bar.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
topMargin: 0
|
||||||
|
}
|
||||||
|
height: (parent.height - top_bar.height) / 1.2
|
||||||
|
width: parent.width / 1.2
|
||||||
|
base_color: root.background_color
|
||||||
|
distance: 0
|
||||||
|
tank_lid_color: "#F0121212"
|
||||||
|
tank_base_color: "#0FFFFFFF"
|
||||||
|
water_level_color: "#F00F7FAF"
|
||||||
|
tank_base_border_color: "#F0121212"
|
||||||
|
min_thres_input: 0
|
||||||
|
max_thres_input: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: status
|
||||||
|
text: root.status
|
||||||
|
anchors {
|
||||||
|
bottom: connect_button.top
|
||||||
|
bottomMargin: 20
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
color: root.accent_color
|
||||||
|
font.pointSize: root.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectButton {
|
||||||
|
id: connect_button
|
||||||
|
height: parent.height / 20
|
||||||
|
width: parent.width / 4
|
||||||
|
font_color: root.accent_color
|
||||||
|
font_enlarged_text_size: root.font_enlarged_text_size
|
||||||
|
font_normal_text_size: root.font_normal_text_size
|
||||||
|
color: root.background_color
|
||||||
|
accent_color: root.accent_color
|
||||||
|
anchors {
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
bottom: parent.bottom
|
||||||
|
bottomMargin: 50
|
||||||
|
}
|
||||||
|
onConnect: {
|
||||||
|
requester.start();
|
||||||
|
root.status = "Waiting to Send.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestWorker {
|
||||||
|
id: requester
|
||||||
|
onStatusChanged: () => {
|
||||||
|
root.status = requester.status;
|
||||||
|
}
|
||||||
|
onNetworkReply: (water_level, fill_rate, expected) => {
|
||||||
|
tank.setDistance(water_level);
|
||||||
|
tank.rate_of_fill = fill_rate;
|
||||||
|
tank.expected_to_fill = expected;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ConfigInterface {
|
||||||
|
id: config_interface
|
||||||
|
fileName: "config.json"
|
||||||
|
onConfig: (config) => {
|
||||||
|
settings_popup.config = config
|
||||||
|
console.log("Loading initial config");
|
||||||
|
setConfig(config);
|
||||||
|
if(root.status === "Initializing") {
|
||||||
|
root.status = "Waiting to connect.";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setConfig(config) {
|
||||||
|
tank.setConfig(config);
|
||||||
|
requester.setConfig(config);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
46
SettingsBarIcon.qml
Normal file
46
SettingsBarIcon.qml
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: settings_rect
|
||||||
|
signal open;
|
||||||
|
property color accent_color: "#00000000"
|
||||||
|
property int font_enlarged_text_size: 20
|
||||||
|
Behavior on color {
|
||||||
|
ColorAnimation {
|
||||||
|
duration: 200
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: settings_text
|
||||||
|
text: "⚙"
|
||||||
|
anchors.centerIn: parent
|
||||||
|
color: settings_rect.accent_color
|
||||||
|
font.pointSize: settings_rect.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_rect.open();
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
settings_rect.color = settings_rect_pressed_prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
251
SettingsPopup.qml
Normal file
251
SettingsPopup.qml
Normal file
|
|
@ -0,0 +1,251 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
Popup {
|
||||||
|
id: settings_popup
|
||||||
|
signal saveConfig(config: var)
|
||||||
|
property color accent_color: "#00000000"
|
||||||
|
property int font_enlarged_text_size: 20
|
||||||
|
property int font_normal_text_size: 16
|
||||||
|
property var config: {
|
||||||
|
"max": 0,
|
||||||
|
"min": 0,
|
||||||
|
"alert": 0,
|
||||||
|
"interval": 0,
|
||||||
|
"url": "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
background: Rectangle {
|
||||||
|
color: "#F0121212"
|
||||||
|
radius: 5
|
||||||
|
border.color: settings_popup.accent_color
|
||||||
|
border.width: 1
|
||||||
|
}
|
||||||
|
height: Math.max(parent.height / 1.2, contentHeight)
|
||||||
|
width: parent.width / 1.1
|
||||||
|
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: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_enlarged_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: max_thres_label
|
||||||
|
text: "Max Threshold: "
|
||||||
|
anchors {
|
||||||
|
topMargin: 20
|
||||||
|
top: settings_label.bottom
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: max_thres_input.verticalCenter
|
||||||
|
}
|
||||||
|
height: max_thres_input.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: max_thres_input
|
||||||
|
text: config.max
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 5
|
||||||
|
top: settings_label.bottom
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
width: parent.width / 2
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: min_thres_label
|
||||||
|
text: "Min Threshold: "
|
||||||
|
anchors {
|
||||||
|
topMargin: 20
|
||||||
|
top: max_thres_label.bottom
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: min_thres_input.verticalCenter
|
||||||
|
}
|
||||||
|
height: min_thres_input.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: min_thres_input
|
||||||
|
text: config.min
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 5
|
||||||
|
top: max_thres_label.bottom
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
width: parent.width / 2
|
||||||
|
color: root.accent_color
|
||||||
|
font.pointSize: root.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: alert_thres_label
|
||||||
|
text: "Alert Percentage: "
|
||||||
|
anchors {
|
||||||
|
topMargin: 20
|
||||||
|
top: min_thres_label.bottom
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: alert_thres_input.verticalCenter
|
||||||
|
}
|
||||||
|
height: alert_thres_input.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: alert_thres_input
|
||||||
|
text: config.alert
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 5
|
||||||
|
top: min_thres_label.bottom
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
width: parent.width / 2
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: interval_label
|
||||||
|
text: "Update Interval: "
|
||||||
|
anchors {
|
||||||
|
topMargin: 20
|
||||||
|
top: alert_thres_label.bottom
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: interval_input.verticalCenter
|
||||||
|
}
|
||||||
|
height: interval_input.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: interval_input
|
||||||
|
text: config.interval
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 5
|
||||||
|
top: alert_thres_input.bottom
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
width: parent.width / 2
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: http_url_label
|
||||||
|
text: "Url: "
|
||||||
|
anchors {
|
||||||
|
topMargin: 20
|
||||||
|
top: interval_label.bottom
|
||||||
|
left: parent.left
|
||||||
|
verticalCenter: http_url_input.verticalCenter
|
||||||
|
}
|
||||||
|
height: http_url_input.height
|
||||||
|
verticalAlignment: Text.AlignVCenter
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
}
|
||||||
|
|
||||||
|
TextField {
|
||||||
|
id: http_url_input
|
||||||
|
text: config.url
|
||||||
|
anchors {
|
||||||
|
right: parent.right
|
||||||
|
rightMargin: 20
|
||||||
|
leftMargin: 20
|
||||||
|
top: interval_input.bottom
|
||||||
|
topMargin: 20
|
||||||
|
}
|
||||||
|
width: Math.max(parent.width / 2, parent.width - http_url_label.width - anchors.leftMargin)
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.font_normal_text_size
|
||||||
|
cursorPosition: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: save_config
|
||||||
|
anchors {
|
||||||
|
topMargin: 10
|
||||||
|
bottom: parent.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
color: "#F0121212"
|
||||||
|
width: parent.width / 1.3
|
||||||
|
height: 60
|
||||||
|
border {
|
||||||
|
width: 1
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
}
|
||||||
|
radius: 3
|
||||||
|
|
||||||
|
Text {
|
||||||
|
anchors.centerIn: parent
|
||||||
|
text: "Save"
|
||||||
|
color: settings_popup.accent_color
|
||||||
|
font.pointSize: settings_popup.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(max_thres_input.text);
|
||||||
|
let min = parseFloat(min_thres_input.text);
|
||||||
|
let alert = parseFloat(alert_thres_input.text);
|
||||||
|
let url = http_url_input.text;
|
||||||
|
let interval = parseInt(interval_input.text);
|
||||||
|
var new_config = {"max": max, "min": min, "alert": alert, "url": url, "interval": interval}
|
||||||
|
settings_popup.saveConfig(new_config)
|
||||||
|
settings_popup.close();
|
||||||
|
}
|
||||||
|
onReleased: {
|
||||||
|
save_config.color = settings_rect_pressed_prev
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
162
WaterTank.qml
Normal file
162
WaterTank.qml
Normal file
|
|
@ -0,0 +1,162 @@
|
||||||
|
import QtQuick
|
||||||
|
import QtQuick.Effects
|
||||||
|
import QtQuick.Controls
|
||||||
|
import QtQuick.Controls.Material
|
||||||
|
|
||||||
|
Item {
|
||||||
|
id: root
|
||||||
|
property color base_color: "#000F0F0F"
|
||||||
|
property color tank_lid_color: "#F0121212"
|
||||||
|
property color tank_base_color: "#00FFFFFF"
|
||||||
|
property color water_level_color: "#F00F7FAF"
|
||||||
|
property color tank_base_border_color: "#F0121212"
|
||||||
|
property color text_color: "#F05FAF5F"
|
||||||
|
property double height_max: tank_base.height - 10
|
||||||
|
property double current_level: 0
|
||||||
|
property double percentage_filled: 0
|
||||||
|
property double distance: 0
|
||||||
|
property double min_thres_input: 0
|
||||||
|
property double max_thres_input: 0
|
||||||
|
property double rate_of_fill: 0
|
||||||
|
property double expected_to_fill: 0
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: base
|
||||||
|
color: root.base_color
|
||||||
|
anchors.fill: parent
|
||||||
|
Rectangle {
|
||||||
|
id: tank_lid
|
||||||
|
color: root.tank_lid_color
|
||||||
|
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: root.tank_base_color
|
||||||
|
anchors.centerIn: parent
|
||||||
|
width: parent.width / 2
|
||||||
|
height: parent.height / 2
|
||||||
|
border.color: root.tank_base_border_color
|
||||||
|
border.width: 5
|
||||||
|
radius: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: water_level
|
||||||
|
color: root.water_level_color
|
||||||
|
height: root.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: water_level_reading
|
||||||
|
text: "Current Reading: " + root.distance + " cm"
|
||||||
|
font.pointSize: 16
|
||||||
|
color: root.text_color
|
||||||
|
anchors {
|
||||||
|
margins: 5
|
||||||
|
top: tank_base.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: water_level_percentage
|
||||||
|
text: "Percentage: " + root.percentage_filled + " %"
|
||||||
|
font.pointSize: 16
|
||||||
|
color: root.text_color
|
||||||
|
anchors {
|
||||||
|
margins: 5
|
||||||
|
top: water_level_reading.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: fill_status
|
||||||
|
text: "Filling status: " + (( root.rate_of_fill > 0)? "Filling" : "Discharging")
|
||||||
|
font.pointSize: 16
|
||||||
|
color: root.text_color
|
||||||
|
anchors {
|
||||||
|
margins: 5
|
||||||
|
top: water_level_percentage.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: rate_of_fill_label
|
||||||
|
text: "Rate of fill: " + root.rate_of_fill + " cm/s"
|
||||||
|
font.pointSize: 16
|
||||||
|
color: root.text_color
|
||||||
|
anchors {
|
||||||
|
margins: 5
|
||||||
|
top: fill_status.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
Text {
|
||||||
|
id: expected_to_fill_label
|
||||||
|
text: "Expected to fill in: " + root.expected_to_fill + " s"
|
||||||
|
font.pointSize: 16
|
||||||
|
color: root.text_color
|
||||||
|
visible: (root.rate_of_fill > 1) ? true : false
|
||||||
|
anchors {
|
||||||
|
margins: 5
|
||||||
|
top: rate_of_fill_label.bottom
|
||||||
|
horizontalCenter: parent.horizontalCenter
|
||||||
|
}
|
||||||
|
horizontalAlignment: Text.AlignHCenter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function setConfig(config: var) {
|
||||||
|
min_thres_input = config.min;
|
||||||
|
max_thres_input = config.max;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setDistance(distance) {
|
||||||
|
root.distance = distance;
|
||||||
|
console.log("Distance changed to: ", distance);
|
||||||
|
let current_min_reading = Math.max(0, min_thres_input);
|
||||||
|
let effective_max_reading = Math.max(0, max_thres_input) - current_min_reading;
|
||||||
|
let current_reading = Math.max(0, root.distance - current_min_reading);
|
||||||
|
console.log("Current reading is: " + current_reading)
|
||||||
|
console.log("Current min reading is: " + current_min_reading)
|
||||||
|
console.log("Effective max reading is: " + effective_max_reading)
|
||||||
|
|
||||||
|
let height_max_possible = root.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;
|
||||||
|
console.log("Current reading is: " + current_reading)
|
||||||
|
console.log("Normalized height is: " + normalized_height)
|
||||||
|
console.log("Percentage filled is: " + percentage_filled)
|
||||||
|
root.current_level = normalized_height;
|
||||||
|
root.percentage_filled = percentage_filled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
53
configinterface.cpp
Normal file
53
configinterface.cpp
Normal file
|
|
@ -0,0 +1,53 @@
|
||||||
|
#include "configinterface.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
ConfigInterface::ConfigInterface(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ConfigInterface::fileName() const
|
||||||
|
{
|
||||||
|
return m_fileName;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigInterface::setFileName(const QString &newFileName)
|
||||||
|
{
|
||||||
|
if (m_fileName == newFileName)
|
||||||
|
return;
|
||||||
|
m_fileName = newFileName;
|
||||||
|
std::fstream input(newFileName.toStdString(), std::fstream::in);
|
||||||
|
if(!input.is_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string data;
|
||||||
|
std::string temp;
|
||||||
|
while(std::getline(input, temp)) {
|
||||||
|
data += temp;
|
||||||
|
}
|
||||||
|
input.flush();
|
||||||
|
input.close();
|
||||||
|
QByteArray arr_data(data);
|
||||||
|
QJsonDocument doc = QJsonDocument::fromJson(arr_data);
|
||||||
|
// The following works even the qml part is not initailzed/connected
|
||||||
|
// do not change back to just emit config()
|
||||||
|
QMetaObject::invokeMethod(this, &ConfigInterface::config, Qt::QueuedConnection, doc.object());
|
||||||
|
emit fileNameChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ConfigInterface::saveConfig(QJsonObject config)
|
||||||
|
{
|
||||||
|
QJsonDocument doc;
|
||||||
|
doc.setObject(config);
|
||||||
|
std::string data = doc.toJson().toStdString();
|
||||||
|
std::fstream output(m_fileName.toStdString(), std::fstream::out);
|
||||||
|
if(!output.is_open()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
output << data;
|
||||||
|
output.flush();
|
||||||
|
output.close();
|
||||||
|
QMetaObject::invokeMethod(this, &ConfigInterface::config, Qt::QueuedConnection, doc.object());
|
||||||
|
}
|
||||||
28
configinterface.h
Normal file
28
configinterface.h
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
#ifndef CONFIGINTERFACE_H
|
||||||
|
#define CONFIGINTERFACE_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonObject>
|
||||||
|
|
||||||
|
class ConfigInterface : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
Q_PROPERTY(QString fileName READ fileName WRITE setFileName NOTIFY fileNameChanged FINAL)
|
||||||
|
public:
|
||||||
|
explicit ConfigInterface(QObject *parent = nullptr);
|
||||||
|
QString fileName() const;
|
||||||
|
void setFileName(const QString &newFileName);
|
||||||
|
signals:
|
||||||
|
void config(QJsonObject config);
|
||||||
|
void fileNameChanged();
|
||||||
|
public slots:
|
||||||
|
void saveConfig(QJsonObject config);
|
||||||
|
private:
|
||||||
|
QString m_fileName;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // CONFIGINTERFACE_H
|
||||||
18
main.cpp
Normal file
18
main.cpp
Normal file
|
|
@ -0,0 +1,18 @@
|
||||||
|
#include <QGuiApplication>
|
||||||
|
#include <QQmlApplicationEngine>
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
QGuiApplication app(argc, argv);
|
||||||
|
|
||||||
|
QQmlApplicationEngine engine;
|
||||||
|
QObject::connect(
|
||||||
|
&engine,
|
||||||
|
&QQmlApplicationEngine::objectCreationFailed,
|
||||||
|
&app,
|
||||||
|
[]() { QCoreApplication::exit(-1); },
|
||||||
|
Qt::QueuedConnection);
|
||||||
|
engine.loadFromModule("WaterLevelRewrite", "Main");
|
||||||
|
|
||||||
|
return QCoreApplication::exec();
|
||||||
|
}
|
||||||
112
requestworker.cpp
Normal file
112
requestworker.cpp
Normal file
|
|
@ -0,0 +1,112 @@
|
||||||
|
#include "requestworker.h"
|
||||||
|
#include <QJsonArray>
|
||||||
|
#include <QJsonValue>
|
||||||
|
#include <QJsonObject>
|
||||||
|
#include <QJsonDocument>
|
||||||
|
|
||||||
|
RequestWorker::RequestWorker(QObject *parent)
|
||||||
|
: QObject{parent}
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::setConfig(QJsonObject config)
|
||||||
|
{
|
||||||
|
this->url = config["url"].toString();
|
||||||
|
this->interval = config["interval"].toInt();
|
||||||
|
this->max_height = config["max"].toInt();
|
||||||
|
this->min_height = config["min"].toInt();
|
||||||
|
qDebug() << "In cpp config values are: ";
|
||||||
|
qDebug() << "Url: " << url;
|
||||||
|
qDebug() << "Interval: " << interval;
|
||||||
|
qDebug() << "Max Height: " << max_height;
|
||||||
|
qDebug() << "Min Height: " << min_height;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::start()
|
||||||
|
{
|
||||||
|
if(manager) {
|
||||||
|
manager->deleteLater();
|
||||||
|
manager = nullptr;
|
||||||
|
}
|
||||||
|
manager = new QNetworkAccessManager();
|
||||||
|
if(timer) {
|
||||||
|
timer->deleteLater();
|
||||||
|
timer = nullptr;
|
||||||
|
}
|
||||||
|
timer = new QTimer(this);
|
||||||
|
timer->setInterval(interval);
|
||||||
|
timer->setSingleShot(true);
|
||||||
|
connect(timer, &QTimer::timeout, this, &RequestWorker::makeRequest);
|
||||||
|
timer->start();
|
||||||
|
stop_timer = false;
|
||||||
|
setStatus("Waiting to send.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::stop()
|
||||||
|
{
|
||||||
|
if(timer->isActive())
|
||||||
|
timer->stop();
|
||||||
|
stop_timer = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::makeRequest()
|
||||||
|
{
|
||||||
|
if(stop_timer)
|
||||||
|
return;
|
||||||
|
QNetworkRequest req;
|
||||||
|
req.setUrl(this->url);
|
||||||
|
disconnect(manager, nullptr, nullptr, nullptr);
|
||||||
|
connect(manager, &QNetworkAccessManager::finished, this, &RequestWorker::requestFinished);
|
||||||
|
manager->setTransferTimeout(15000);
|
||||||
|
manager->get(req);
|
||||||
|
setStatus("Request sent; Waiting for Reply.");
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::requestFinished(QNetworkReply *rep)
|
||||||
|
{
|
||||||
|
QByteArray data = rep->readAll();
|
||||||
|
rep->deleteLater();
|
||||||
|
rep = nullptr;
|
||||||
|
auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
auto time_diff = std::chrono::duration_cast<std::chrono::microseconds>(now - prev_time);
|
||||||
|
unsigned int time_in_microseconds = time_diff.count();
|
||||||
|
QJsonObject jdata = QJsonDocument::fromJson(data).object();
|
||||||
|
auto data_water_level = jdata["water_level"].toInt();
|
||||||
|
float water_level = (float) ((float) (data_water_level) / 10.0);
|
||||||
|
float rate_of_fill = ( float )(water_level - prev_distance) /(float) time_in_microseconds;
|
||||||
|
rate_of_fill = rate_of_fill * 1'000'000;
|
||||||
|
int expected_to_fill = (max_height - water_level) / rate_of_fill;
|
||||||
|
if(reply_number == 0) {
|
||||||
|
rate_of_fill = 0;
|
||||||
|
expected_to_fill = 0;
|
||||||
|
}
|
||||||
|
reply_number = 1;
|
||||||
|
qDebug() << "Got water level: " << water_level;
|
||||||
|
qDebug() << "Got rate of fill: " << rate_of_fill;
|
||||||
|
qDebug() << "Got expected to fill: " << expected_to_fill;
|
||||||
|
qDebug() << "Time difference: " << time_in_microseconds;
|
||||||
|
prev_distance = water_level;
|
||||||
|
emit networkReply(water_level, rate_of_fill, expected_to_fill);
|
||||||
|
if(stop_timer) {
|
||||||
|
setStatus("Timer stopped.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
prev_time = now;
|
||||||
|
timer->start();
|
||||||
|
QString status = "Got Reply on: " + QDateTime::currentDateTime().toString("hh:mm:ss") + "; Waiting to Send.";
|
||||||
|
setStatus(status);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString RequestWorker::status() const
|
||||||
|
{
|
||||||
|
return m_status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RequestWorker::setStatus(const QString &newStatus)
|
||||||
|
{
|
||||||
|
if (m_status == newStatus)
|
||||||
|
return;
|
||||||
|
m_status = newStatus;
|
||||||
|
emit statusChanged();
|
||||||
|
}
|
||||||
49
requestworker.h
Normal file
49
requestworker.h
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
#ifndef REQUESTWORKER_H
|
||||||
|
#define REQUESTWORKER_H
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QQmlEngine>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
|
#include <QNetworkRequest>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QTimer>
|
||||||
|
#include <QThread>
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
class RequestWorker : public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
QML_ELEMENT
|
||||||
|
Q_PROPERTY(QString status READ status WRITE setStatus NOTIFY statusChanged FINAL)
|
||||||
|
QNetworkAccessManager *manager = nullptr;
|
||||||
|
QTimer *timer = nullptr;
|
||||||
|
QString url;
|
||||||
|
int interval;
|
||||||
|
float prev_distance = 0;
|
||||||
|
int max_height = 0;
|
||||||
|
int min_height = 0;
|
||||||
|
bool stop_timer = false;
|
||||||
|
int reply_number = 0;
|
||||||
|
std::chrono::time_point<std::chrono::high_resolution_clock> prev_time;
|
||||||
|
public:
|
||||||
|
explicit RequestWorker(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
QString status() const;
|
||||||
|
void setStatus(const QString &newStatus);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void networkReply(float distance, float rate_of_fill, int expected_to_fill);
|
||||||
|
void statusChanged();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setConfig(QJsonObject config);
|
||||||
|
void start();
|
||||||
|
void stop();
|
||||||
|
private slots:
|
||||||
|
void makeRequest();
|
||||||
|
void requestFinished(QNetworkReply *rep);
|
||||||
|
private:
|
||||||
|
QString m_status;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // REQUESTWORKER_H
|
||||||
Loading…
Reference in a new issue