mirror of
https://github.com/janishutz/BiogasControllerApp.git
synced 2025-11-25 05:44:23 +00:00
Redesign app, prepare for 3.1.0 release
This commit is contained in:
123
gui/main/main.kv
123
gui/main/main.kv
@@ -1,101 +1,124 @@
|
||||
<MainScreen>:
|
||||
on_pre_enter: root.reset()
|
||||
name: "main"
|
||||
canvas.before:
|
||||
Color:
|
||||
rgba: (10,10,10,0.1)
|
||||
Rectangle:
|
||||
size: self.size
|
||||
pos: self.pos
|
||||
GridLayout:
|
||||
FloatLayout:
|
||||
Label:
|
||||
pos_hint: {"y":0.4}
|
||||
text: "READOUT"
|
||||
font_size: 40
|
||||
color: (0, 113, 0, 1)
|
||||
bold: True
|
||||
GridLayout:
|
||||
MDFloatLayout:
|
||||
MDGridLayout:
|
||||
cols: 1
|
||||
pos_hint: {'x': 0, 'y': 0.4}
|
||||
MDLabel:
|
||||
text: "READOUT"
|
||||
font_size: 40
|
||||
halign: 'center'
|
||||
valign: 'center'
|
||||
pos_hint: {'center_x': 0, 'center_y': 0}
|
||||
bold: True
|
||||
|
||||
MDGridLayout:
|
||||
cols:4
|
||||
size_hint: 0.8, 0.3
|
||||
pos_hint: {"x":0.1, "y":0.4}
|
||||
Label:
|
||||
MDLabel:
|
||||
text: "Sensor 1: "
|
||||
font_size: 20
|
||||
Label:
|
||||
MDLabel:
|
||||
id: sensor1
|
||||
text: ""
|
||||
size_hint: 1, 1
|
||||
halign: 'left'
|
||||
text_size: self.size
|
||||
Label:
|
||||
MDLabel:
|
||||
text: "Sensor 2: "
|
||||
font_size: 20
|
||||
Label:
|
||||
MDLabel:
|
||||
id: sensor2
|
||||
text: ""
|
||||
size_hint: 1, 1
|
||||
halign: 'left'
|
||||
text_size: self.size
|
||||
Label:
|
||||
MDLabel:
|
||||
text: "Sensor 3: "
|
||||
font_size: 20
|
||||
Label:
|
||||
MDLabel:
|
||||
id: sensor3
|
||||
text: ""
|
||||
size_hint: 1, 1
|
||||
halign: 'left'
|
||||
text_size: self.size
|
||||
Label:
|
||||
MDLabel:
|
||||
text: "Sensor 4: "
|
||||
font_size: 20
|
||||
Label:
|
||||
MDLabel:
|
||||
id: sensor4
|
||||
text: ""
|
||||
size_hint: 1, 1
|
||||
halign: 'left'
|
||||
text_size: self.size
|
||||
Button:
|
||||
MDFillRoundFlatButton:
|
||||
text: "Connect"
|
||||
size_hint: 0.2, 0.1
|
||||
pos_hint: {"x": 0.5, "y": 0.05}
|
||||
background_color: (255, 0, 0, 0.6)
|
||||
size_hint: 0.15, 0.09
|
||||
pos_hint: {"x": 0.03, "y": 0.05}
|
||||
on_release:
|
||||
root.start()
|
||||
Button:
|
||||
|
||||
MDFillRoundFlatButton:
|
||||
text: "Disconnect"
|
||||
size_hint: 0.2, 0.1
|
||||
pos_hint: {"x": 0.7, "y": 0.05}
|
||||
background_color: (255, 0, 0, 0.6)
|
||||
size_hint: 0.15, 0.09
|
||||
pos_hint: {"x": 0.2, "y": 0.05}
|
||||
on_release:
|
||||
root.end()
|
||||
Button:
|
||||
|
||||
MDFillRoundFlatButton:
|
||||
text: "Back"
|
||||
size_hint: 0.3, 0.1
|
||||
pos_hint: {"x":0.05, "y":0.05}
|
||||
background_color: (255, 0, 0, 0.6)
|
||||
size_hint: 0.15, 0.09
|
||||
pos_hint: {"right": 0.95, "y":0.05}
|
||||
md_bg_color: app.theme_cls.primary_dark
|
||||
on_release:
|
||||
root.end()
|
||||
app.root.current = "home"
|
||||
root.manager.transition.direction = "left"
|
||||
ToggleButton:
|
||||
id: mode_selector
|
||||
|
||||
MDGridLayout:
|
||||
cols: 2
|
||||
size_hint: 0.15, 0.1
|
||||
pos_hint: {"x":0.1, "y":0.2}
|
||||
text: "Normal Mode" if self.state == "normal" else "Fast Mode"
|
||||
on_text: root.switch_mode(mode_selector.text)
|
||||
background_color: (255,0,0,0.6) if self.state == "normal" else (0,0,255,0.6)
|
||||
Button:
|
||||
pos_hint: {"x":0.1, "y":0.15}
|
||||
MDLabel:
|
||||
text: "Fast Mode"
|
||||
valign: "center"
|
||||
MDSwitch:
|
||||
id: mode_selector
|
||||
on_active: root.switch_mode()
|
||||
icon_active: "check"
|
||||
|
||||
MDFillRoundFlatButton:
|
||||
text: "Configuration"
|
||||
size_hint: 0.15, 0.1
|
||||
pos_hint: {"x":0.7, "y":0.2}
|
||||
background_color: (255, 0, 0, 0.6)
|
||||
size_hint: 0.1, 0.07
|
||||
pos_hint: {"x":0.45, "y":0.06}
|
||||
md_bg_color: app.theme_cls.accent_dark
|
||||
on_release:
|
||||
root.end()
|
||||
app.root.current = "program"
|
||||
root.manager.transition.direction = "down"
|
||||
Label:
|
||||
id: status
|
||||
text: "Status will appear here"
|
||||
font_size: 10
|
||||
pos_hint: {"x":0.4, "y": 0.3}
|
||||
|
||||
MDGridLayout:
|
||||
size_hint: 0.2, None
|
||||
spacing: 0
|
||||
padding: 0
|
||||
cols: 1
|
||||
pos_hint: {'right': 0.95, 'top': 0.95}
|
||||
MDLabel:
|
||||
id: status
|
||||
text: "Status will appear here"
|
||||
font_size: 10
|
||||
halign: 'right'
|
||||
|
||||
MDGridLayout:
|
||||
size_hint: None, None
|
||||
spacing: 0
|
||||
padding: 0
|
||||
cols: 1
|
||||
pos_hint: {'right': 0.95, 'top': 0.925}
|
||||
MDLabel:
|
||||
id: port
|
||||
text: "Port: Not connected"
|
||||
font_size: 10
|
||||
halign: 'right'
|
||||
|
||||
@@ -3,8 +3,9 @@ from time import time
|
||||
from typing import List, override
|
||||
from kivymd.uix.screen import MDScreen
|
||||
from kivy.lang import Builder
|
||||
from gui.popups.popups import SingleRowPopup, TwoActionPopup, empty_func
|
||||
from kivy.clock import Clock, ClockEvent
|
||||
from kivymd.uix.button import MDFlatButton
|
||||
from kivymd.uix.dialog import MDDialog
|
||||
import queue
|
||||
import threading
|
||||
|
||||
@@ -53,7 +54,7 @@ class ReaderThread(threading.Thread):
|
||||
# Hook to output stream
|
||||
if self._instructions.hook_main():
|
||||
# We are now hooked to the stream (i.e. data is synced)
|
||||
synced_queue.put(["HOOK"])
|
||||
synced_queue.put(["HOOK", self._com.get_comport()])
|
||||
|
||||
# making it exit using the stop function
|
||||
while self._run:
|
||||
@@ -70,17 +71,22 @@ class ReaderThread(threading.Thread):
|
||||
for i in range(4):
|
||||
# The slicing that happens here uses offsets automatically calculated from the sensor id
|
||||
# This allows for short code
|
||||
data.append(
|
||||
f"Tadc: {
|
||||
self._decoder.decode_int(received[12 * i:12 * i + 4])
|
||||
}\nTemp: {
|
||||
round(self._decoder.decode_float(received[12 * i + 5:12 * i + 11]) * 1000) / 1000
|
||||
}°C\nDC: {
|
||||
round((self._decoder.decode_float_long(received[48 + 5 * i: 52 + 5 * i]) / 65535.0 * 100) * 1000) / 1000
|
||||
}%"
|
||||
)
|
||||
try:
|
||||
data.append(
|
||||
f"Tadc: {
|
||||
self._decoder.decode_int(received[12 * i:12 * i + 4])
|
||||
}\nTemp: {
|
||||
round(self._decoder.decode_float(received[12 * i + 5:12 * i + 11]) * 1000) / 1000
|
||||
}°C\nDC: {
|
||||
round((self._decoder.decode_float_long(received[48 + 5 * i: 52 + 5 * i]) / 65535.0 * 100) * 1000) / 1000
|
||||
}%"
|
||||
)
|
||||
except:
|
||||
data.append("Bad data")
|
||||
# Calculate the frequency of updates
|
||||
data.append(str(round((1 / (time() - start_time)) * 1000) / 1000) + " Hz")
|
||||
data.append(
|
||||
str(round((1 / (time() - start_time)) * 1000) / 1000) + " Hz"
|
||||
)
|
||||
synced_queue.put(data)
|
||||
else:
|
||||
# Send error message to the UI updater
|
||||
@@ -104,6 +110,30 @@ class MainScreen(MDScreen):
|
||||
# Set some variables
|
||||
self._com = com
|
||||
self._event = None
|
||||
self._fast_mode = False
|
||||
|
||||
self.connection_error_dialog = MDDialog(
|
||||
title="Connection",
|
||||
text="Failed to connect. Do you wish to retry?",
|
||||
buttons=[
|
||||
MDFlatButton(
|
||||
text="Cancel",
|
||||
on_release=lambda _: self.connection_error_dialog.dismiss(),
|
||||
),
|
||||
MDFlatButton(text="Retry", on_release=lambda _: self.start()),
|
||||
],
|
||||
)
|
||||
|
||||
self.mode_switch_error_dialog = MDDialog(
|
||||
title="Mode Switch",
|
||||
text="Failed to change mode. Please try again",
|
||||
buttons=[
|
||||
MDFlatButton(
|
||||
text="Ok",
|
||||
on_release=lambda _: self.mode_switch_error_dialog.dismiss(),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# Prepare the reader thread
|
||||
self._prepare_reader()
|
||||
@@ -115,36 +145,31 @@ class MainScreen(MDScreen):
|
||||
|
||||
def _prepare_reader(self):
|
||||
self._reader = ReaderThread()
|
||||
self._reader.setDaemon(True)
|
||||
self._reader.daemon = True
|
||||
self._reader.set_com(self._com)
|
||||
|
||||
# Start the connection to the micro-controller to read data from it.
|
||||
# This also now starts the reader thread to continuously read out data
|
||||
def start(self):
|
||||
# Prevent running multiple times
|
||||
self.connection_error_dialog.dismiss()
|
||||
if self._has_connected:
|
||||
return
|
||||
|
||||
self.ids.status.text = "Connecting..."
|
||||
if self._com.connect():
|
||||
print("Acquired connection")
|
||||
print("[ COM ] Connection Acquired")
|
||||
self._has_connected = True
|
||||
self._has_run = True
|
||||
if self._has_run:
|
||||
self._prepare_reader()
|
||||
# Start communication
|
||||
self._reader.start()
|
||||
print("Reader has started")
|
||||
print("[ COM ] Reader has started")
|
||||
self._event = Clock.schedule_interval(self._update_screen, 0.5)
|
||||
else:
|
||||
self.ids.status.text = "Connection failed"
|
||||
TwoActionPopup().open(
|
||||
"Failed to connect. Do you want to retry?",
|
||||
"Cancel",
|
||||
empty_func,
|
||||
"Retry",
|
||||
self.start,
|
||||
)
|
||||
self.connection_error_dialog.open()
|
||||
|
||||
# End connection to micro-controller and set it back to normal mode
|
||||
def end(self, set_msg: bool = True):
|
||||
@@ -166,10 +191,12 @@ class MainScreen(MDScreen):
|
||||
self._com.close()
|
||||
if set_msg:
|
||||
self.ids.status.text = "Connection terminated"
|
||||
self.ids.port.text = "Port: Not connected"
|
||||
self._has_connected = False
|
||||
print("Connection terminated")
|
||||
|
||||
# A helper function to update the screen. Is called on an interval
|
||||
def _update_screen(self, dt):
|
||||
def _update_screen(self, _):
|
||||
update = []
|
||||
try:
|
||||
update = synced_queue.get_nowait()
|
||||
@@ -182,8 +209,10 @@ class MainScreen(MDScreen):
|
||||
if update[0] == "ERR_HOOK":
|
||||
self.ids.status.text = "Hook failed"
|
||||
self.end(False)
|
||||
elif update[0] == "HOOK":
|
||||
if len(update) == 2:
|
||||
if update[0] == "HOOK":
|
||||
self.ids.status.text = "Connected to controller"
|
||||
self.ids.port.text = "Port: " + update[1]
|
||||
else:
|
||||
self.ids.sensor1.text = update[0]
|
||||
self.ids.sensor2.text = update[1]
|
||||
@@ -198,9 +227,10 @@ class MainScreen(MDScreen):
|
||||
self.ids.sensor3.text = ""
|
||||
self.ids.sensor4.text = ""
|
||||
self.ids.status.text = "Status will appear here"
|
||||
self.ids.port.text = "Port: Not connected"
|
||||
|
||||
# Switch the mode for the micro-controller
|
||||
def switch_mode(self, new_mode: str):
|
||||
def switch_mode(self):
|
||||
# Store if we have been connected to the micro-controller before mode was switched
|
||||
was_connected = self._has_connected
|
||||
|
||||
@@ -210,12 +240,12 @@ class MainScreen(MDScreen):
|
||||
|
||||
# Try to set the new mode
|
||||
try:
|
||||
if new_mode == "Normal Mode":
|
||||
if self._fast_mode:
|
||||
self._com.send("NM")
|
||||
else:
|
||||
self._com.send("FM")
|
||||
except:
|
||||
SingleRowPopup().open("Failed to switch modes")
|
||||
self.mode_switch_error_dialog.open()
|
||||
return
|
||||
|
||||
self.ids.status.text = "Mode set"
|
||||
|
||||
Reference in New Issue
Block a user