Config, Lots of docs, Format

Added a config validator and documented code that was previously
undocumented, for the plot_generator scripts, documented them.
This commit is contained in:
2025-06-16 16:36:18 +02:00
parent 3a6cd6af3d
commit 7905cb851a
14 changed files with 436 additions and 89 deletions

5
gui/README.md Normal file
View File

@@ -0,0 +1,5 @@
# GUI
This folder contains all files that are used for the GUI of the app.
It is written in KivyMD, so if you don't know what that is and you don't want to learn it,
there isn't much of use in here for you! - Just so you're warned

View File

@@ -4,25 +4,35 @@ from kivymd.uix.button import MDFlatButton
from kivy.lang import Builder
import webbrowser
# Simple about screen
class AboutScreen(Screen):
def __init__(self, **kw):
# Prepare dialog
self.opened_web_browser_dialog = MDDialog(
title="Open Link",
text="Your webbrowser has been opened. Continue there",
buttons=[
MDFlatButton(text="Ok", on_release=lambda _: self.opened_web_browser_dialog.dismiss()),
MDFlatButton(
text="Ok",
on_release=lambda _: self.opened_web_browser_dialog.dismiss(),
),
],
)
super().__init__(**kw)
def goto(self, loc: str):
# Open web browser with links
if loc == "wiki":
webbrowser.open('https://github.com/janishutz/BiogasControllerApp/wiki', new=2)
webbrowser.open(
"https://github.com/janishutz/BiogasControllerApp/wiki", new=2
)
elif loc == "issues":
webbrowser.open('https://github.com/janishutz/BiogasControllerApp/issues', new=2)
webbrowser.open(
"https://github.com/janishutz/BiogasControllerApp/issues", new=2
)
elif loc == "repo":
webbrowser.open('https://github.com/janishutz/BiogasControllerApp', new=2)
webbrowser.open("https://github.com/janishutz/BiogasControllerApp", new=2)
self.opened_web_browser_dialog.open()
Builder.load_file('./gui/about/about.kv')
Builder.load_file("./gui/about/about.kv")

View File

@@ -1,3 +1,4 @@
from kivy.base import Clock
from kivymd.app import MDApp
from kivymd.uix.button import MDFlatButton
from kivymd.uix.dialog import MDDialog
@@ -48,9 +49,7 @@ class HomeScreen(MDScreen):
text="Cancel",
on_release=lambda _: self.quit_dialog.dismiss(),
),
MDFlatButton(
text="Quit", on_release=lambda _: self._quit()
),
MDFlatButton(text="Quit", on_release=lambda _: self._quit()),
],
)
super().__init__(**kw)
@@ -59,9 +58,12 @@ class HomeScreen(MDScreen):
self._com.close()
MDApp.get_running_app().stop()
def start(self):
Clock.schedule_once(lambda _: self._start())
# Go to the main screen if we can establish connection or the check was disabled
# in the configs
def start(self):
def _start(self):
if self._com.connect():
self.manager.current = "main"
self.manager.transition.direction = "right"
@@ -94,7 +96,6 @@ class HomeScreen(MDScreen):
"13"
] = f"Incorrect permissions at {port}. Resolve by running 'sudo chmod 777 {port}'"
if port == "":
return information[operating_system]["NO_COM"]

View File

@@ -112,6 +112,7 @@ class MainScreen(MDScreen):
self._event = None
self._fast_mode = False
# Set up Dialog for erros
self.connection_error_dialog = MDDialog(
title="Connection",
text="Failed to connect. Do you wish to retry?",
@@ -144,28 +145,39 @@ class MainScreen(MDScreen):
super().__init__(**kw)
def _prepare_reader(self):
# Prepares the reader thread
self._reader = ReaderThread()
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
# Small helper function that makes the UI not freeze by offloading
def start(self):
Clock.schedule_once(lambda _: self._start())
# Start the connection to the micro-controller to read data from it.
# This also 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
# Some UI config
self.ids.status.text = "Connecting..."
if self._com.connect():
print("[ COM ] Connection Acquired")
# Prevent multiple connections
self._has_connected = True
self._has_run = True
if self._has_run:
self._prepare_reader()
# Start communication
self._reader.start()
print("[ COM ] Reader has started")
# Schedule UI updates
self._event = Clock.schedule_interval(self._update_screen, 0.5)
else:
self.ids.status.text = "Connection failed"
@@ -179,15 +191,20 @@ class MainScreen(MDScreen):
if self._event != None:
self._event.cancel()
self._reader.stop()
# Join the thread to end it safely
try:
self._reader.join()
except:
pass
# Go back to Normal Mode on the Controller
# This is so you don't accidentally forget!
try:
self._com.send("NM")
except:
pass
self._com.close()
if set_msg:
self.ids.status.text = "Connection terminated"
@@ -202,18 +219,24 @@ class MainScreen(MDScreen):
update = synced_queue.get_nowait()
except:
pass
if len(update) == 0:
# There are no updates to process, don't block and simply try again next time
return
if len(update) == 1:
# Sync errors
if update[0] == "ERR_HOOK":
self.ids.status.text = "Hook failed"
self.end(False)
if len(update) == 2:
# Connection successful
if update[0] == "HOOK":
self.ids.status.text = "Connected to controller"
self.ids.port.text = "Port: " + update[1]
else:
# Update the UI
self.ids.sensor1.text = update[0]
self.ids.sensor2.text = update[1]
self.ids.sensor3.text = update[2]

View File

@@ -20,15 +20,16 @@ class ProgramScreen(MDScreen):
self._instructions = Instructions(com)
self._decoder = Decoder()
# Configure Dialog
self.connection_error_dialog = MDDialog(
title="Connection",
text="Failed to connect. Do you wish to retry?",
buttons=[
MDFlatButton(
text="Cancel",
on_release=lambda dt: self.connection_error_dialog.dismiss(),
on_release=lambda _: self.connection_error_dialog.dismiss(),
),
MDFlatButton(text="Retry", on_release=lambda dt: self._load()),
MDFlatButton(text="Retry", on_release=lambda _: self.load_config()),
],
)
@@ -38,7 +39,7 @@ class ProgramScreen(MDScreen):
buttons=[
MDFlatButton(
text="Ok",
on_release=lambda dt: self.missing_fields_error_dialog.dismiss(),
on_release=lambda _: self.missing_fields_error_dialog.dismiss(),
),
],
)
@@ -49,7 +50,7 @@ class ProgramScreen(MDScreen):
buttons=[
MDFlatButton(
text="Ok",
on_release=lambda dt: self.save_error_dialog.dismiss(),
on_release=lambda _: self.save_error_dialog.dismiss(),
),
],
)
@@ -60,15 +61,16 @@ class ProgramScreen(MDScreen):
buttons=[
MDFlatButton(
text="Ok",
on_release=lambda dt: self.save_success_dialog.dismiss(),
on_release=lambda _: self.save_success_dialog.dismiss(),
),
],
)
super().__init__(**kw)
# Load the config (async to not freeze the UI)
def load_config(self):
Clock.schedule_once(lambda dt: self._load())
Clock.schedule_once(lambda _: self._load())
# Load the current configuration from the micro-controller
def _load(self):
@@ -131,8 +133,11 @@ class ProgramScreen(MDScreen):
return data
# Transmit the changed data to the micro-controller to reconfigure it
def save(self):
Clock.schedule_once(lambda _: self._save())
# Transmit the changed data to the micro-controller to reconfigure it
def _save(self):
self.ids.status.text = "Saving..."
data = self._read_ui()
if data == None:
@@ -140,14 +145,14 @@ class ProgramScreen(MDScreen):
else:
try:
self._instructions.change_config(data)
except Exception as e:
except:
self.save_error_dialog.open()
return
self.save_success_dialog.open()
self.ids.status.text = "Saved!"
Clock.schedule_once(self.reset_update, 5)
def reset_update(self, dt):
def reset_update(self, _):
self.ids.status.text = ""
def validate_float(self, instance):