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