App launching, some porting work complete

This commit is contained in:
2025-03-05 14:07:58 +01:00
parent ffd75d94dc
commit 92fcc4a6e7
20 changed files with 428 additions and 17 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -0,0 +1,52 @@
import os
import configparser
from typing import override
config = configparser.ConfigParser()
config.read('./config.ini')
# Load config and disable kivy log if necessary
if config['Dev Settings']['verbose'] == "True":
pass
else:
os.environ["KIVY_NO_CONSOLELOG"] = "1"
# Load kivy modules
from kivy.core.window import Window, Config
from kivy.uix.screenmanager import ScreenManager
from kivy.app import App
# Load other libraries
import threading
# Store the current app version
app_version = f"{config['Info']['version']}{config['Info']['subVersion']}"
#---------#
# Screens #
#---------#
from gui.home.home import HomeScreen
from gui.credits.credits import CreditsScreen
from gui.settings.settings import SettingsScreen
#----------------#
# Screen Manager #
#----------------#
class BiogasControllerApp(App):
def __init__(self, **kwargs):
super().__init__(**kwargs)
self.screen_manager = ScreenManager()
@override
def build(self):
self.icon = './BiogasControllerAppLogo.png'
self.title = 'BiogasControllerApp-' + app_version
self.screen_manager.add_widget(HomeScreen(name='home'))
self.screen_manager.add_widget(CreditsScreen(name='credits'))
self.screen_manager.add_widget(SettingsScreen(name='settings'))
return self.screen_manager
if __name__ == "__main__":
BiogasControllerApp().run()

View File

@@ -0,0 +1,19 @@
[Port Settings]
specificport = None
[UI Config]
sizeh = 600
sizew = 800
[Dev Settings]
verbose = True
log_level = DEBUG
disableconnectioncheck = False
[License]
show = 1
[Info]
version = V2.3.0
subversion =

View File

@@ -0,0 +1,3 @@
from gui.popups import *

View File

@@ -0,0 +1,27 @@
<CreditsScreen>:
name: "credits"
canvas.before:
Color:
rgba: (50,50,50,0.2)
Rectangle:
size: self.size
pos: self.pos
FloatLayout:
Button:
text: "back"
size_hint: 0.4, 0.2
pos_hint: {"x":0.3, "y":0.1}
on_release:
app.root.current = "settings"
root.manager.transition.direction = "right"
GridLayout:
cols:1
pos_hint:{"x":0.05, "y":0.35}
size_hint: 0.9, 0.5
Label:
text: "This is a rework of the BiogasControllerApp V1, that was originally programmed by S. Reichmuth."
Label:
text: "Written by: Janis Hutz\nDesigned by: Janis Hutz\nDesign language: Kivy"
Label:
text: "This software is free Software licensed under the GPL V3 (GNU General Public License) and as such comes with absolutely no warranty. In return, you can use, modify, distribute or use any of the code of this software in your own project, if you reuse the same license. For more infos, you can find a copy of this license in the project folder."
text_size: self.width, None

View File

@@ -0,0 +1,8 @@
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
class CreditsScreen(Screen):
pass
Builder.load_file('./gui/credits/credits.kv')

View File

@@ -0,0 +1,46 @@
<HomeScreen>:
name: "home"
canvas.before:
Color:
rgba: (50,50,50,0.2)
Rectangle:
size: self.size
pos: self.pos
GridLayout:
cols:1
Label:
text: "BiogasanlageControllerApp"
font_size: 50
color: (0, 113, 0, 1)
bold:True
italic:True
FloatLayout:
GridLayout:
cols: 2
size_hint: 0.8, 0.8
pos_hint: {"x": 0.1, "y": 0.1}
Button:
text: "Start"
background_color: (255, 0, 0, 0.6)
font_size: 30
on_release:
root.start()
Button:
text: "Quit"
background_color: (255, 0, 0, 0.6)
font_size: 30
on_release:
root.quit()
Label:
text: "App version"
id: app_version
font_size: 13
pos_hint: {"y": -0.45, "x":0.05}
Button:
text: "Settings"
font_size: 13
size_hint: 0.07, 0.06
pos_hint: {"x":0.01, "y":0.01}
background_color: (50, 0, 0, 0.2)
on_release:
root.to_settings()

View File

@@ -0,0 +1,17 @@
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
class HomeScreen(Screen):
def start(self):
pass
def quit(self):
pass
def to_settings(self):
self.manager.current = 'settings'
self.manager.transition.direction = 'down'
Builder.load_file('./gui/home/home.kv')

View File

@@ -0,0 +1,105 @@
<SingleRowPopUp>:
title: "INFORMATION"
size_hint: 0.7, 0.5
auto_dismiss: True
GridLayout:
cols: 1
Label:
id: msg
text_size: self.width, None
GridLayout:
cols: 1
Button:
text: "Ok"
on_release:
root.dismiss()
<QuitPopUp>:
title: "BiogasControllerApp"
font_size: 50
size_hint: 0.5, 0.4
auto_dismiss: False
GridLayout:
cols:1
Label:
text: "Are you sure you want to leave?"
font_size: 20
GridLayout:
cols:2
Button:
text: "Yes"
font_size: 15
on_release:
root.quit()
app.stop()
Button:
text: "No"
font_size: 15
on_press:
root.dismiss()
<TwoActionPopup>:
title: "WARNING!"
font_size: 50
size_hint: 0.5, 0.4
auto_dismiss: False
GridLayout:
cols:1
Label:
id: msg
text: "Message"
font_size: 20
GridLayout:
cols:2
Button:
id: btn1
text: "Details"
on_release:
root.action()
Button:
text:"Ok"
on_release:
root.dismiss()
<DualRowPopup>:
title: "Details"
font_size: 50
size_hint: 0.7, 0.6
auto_dismiss: False
GridLayout:
cols:1
Label:
id: msg_title
text: "Message title"
font_size: 20
Label:
id: msg_body
text: "Message body"
font_size: 14
Button:
text:"Ok"
on_release:
root.dismiss()
<LargeTrippleRowPopUp>:
title: "DETAILS"
font_size: 50
size_hint: 1, 0.7
auto_dismiss: False
GridLayout:
Label:
cols:1
id: msg_title
text: "title"
font_size: 20
Label:
id: msg_body
text: "Message"
font_size: 13
Label:
text: msg_extra
font_size: 13
Button:
text:"Ok"
on_release:
root.dismiss()

View File

@@ -0,0 +1,16 @@
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
class HomeScreen(Screen):
def start(self):
pass
def quit(self):
pass
def to_settings(self):
pass
Builder.load_file('./gui/home/home.kv')

View File

@@ -0,0 +1,16 @@
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
class HomeScreen(Screen):
def start(self):
pass
def quit(self):
pass
def to_settings(self):
pass
Builder.load_file('./gui/home/home.kv')

View File

@@ -0,0 +1,37 @@
<SettingsScreen>:
name: "settings"
canvas.before:
Color:
rgba: (50,50,50,0.2)
Rectangle:
size: self.size
pos: self.pos
GridLayout:
cols: 1
Label:
text: "Settings"
font_size: 40
color: (0, 113, 0, 1)
bold: True
FloatLayout:
GridLayout:
pos_hint: {"x":0.05, "y":0.05}
size_hint: 0.9, 0.9
cols: 3
Button:
text: "Back"
background_color: (255,0,0,0.6)
on_release:
app.root.current = "home"
root.manager.transition.direction = "up"
Button:
text: "Report a\nBug"
background_color: (255,0,0,0.6)
on_release:
root.report_issue()
Button:
text: "Credits"
background_color: (255,0,0,0.6)
on_release:
app.root.current = "credits"
root.manager.transition.direction = "left"

View File

@@ -0,0 +1,10 @@
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder
import webbrowser
class SettingsScreen(Screen):
def report_issue(self):
webbrowser.open('https://github.com/janishutz/BiogasControllerApp/issues', new=2)
Builder.load_file('./gui/settings/settings.kv')

View File

@@ -9,11 +9,22 @@ class Com:
self._serial: Optional[serial.Serial] = None
self._filters = filters if filters != None else [ 'USB-Serial Controller', 'Prolific USB-Serial Controller' ]
self._port_override = ''
self._baudrate = 19200
def set_port_override(self, override: str) -> None:
"""Set the port override, to disable port search"""
self._port_override = override
def _connection_check(self) -> bool:
if self._serial == None:
return self._open()
if self._serial != None:
if not self._serial.is_open:
self._serial.open()
return True
else:
return False
def get_comport(self) -> str:
"""Find the comport the microcontroller has attached to"""
if self._port_override != '':
@@ -34,20 +45,24 @@ class Com:
return ''
def connect(self, baud_rate: int, port_override: Optional[str] = None) -> bool:
"""Try to find a comport and connect to the microcontroller. Returns the success as a boolean"""
def _open(self) -> bool:
comport = self.get_comport()
# Comport search returns empty string if search unsuccessful
if comport == '':
try:
self._serial = serial.Serial(comport, baud_rate, timeout=5)
self._serial = serial.Serial(comport, self._baudrate, timeout=5)
except:
return False
return True
else:
return False
def connect(self, baud_rate: int) -> bool:
"""Try to find a comport and connect to the microcontroller. Returns the success as a boolean"""
self._baudrate = baud_rate
return self._connection_check()
def close(self) -> None:
"""Close the serial connection, if possible"""
if self._serial != None:
@@ -58,23 +73,24 @@ class Com:
def receive(self, byte_count: int) -> bytes:
"""Recieve bytes from microcontroller over serial. Returns bytes. Might want to decode using functions from lib.tools"""
if self._serial == None:
self.connect(19200)
self._connection_check()
if self._serial != None:
return self._serial.read(byte_count)
else:
raise Exception('ERR_CONNECTION')
raise Exception('ERR_CONNECTING')
def send(self, msg: str) -> None:
"""Send a string over serial connection."""
if self._serial == None:
self.connect(19200)
"""Send a string over serial connection. Will open a connection if none is available"""
self._connection_check()
if self._serial != None:
self._serial.write(msg.encode())
else:
raise Exception('ERR_CONNECTING')
def send_float(self, msg: float) -> None:
"""Send a float number over serial connection"""
if self._serial == None:
self.connect(19200)
self._connection_check()
if self._serial != None:
self._serial.write(bytearray(struct.pack('>f', msg))[0:3])
else:
raise Exception('ERR_CONNECTING')

View File

@@ -1,22 +1,61 @@
from typing import Optional
import lib.com
import lib.decoder
import time
# TODO: Load filters (for comport search)
com = lib.com.Com()
decoder = lib.decoder.Decoder()
class Instructions:
def __init__(self) -> None:
pass
def set_port_override(self, override: str) -> None:
com.set_port_override(override)
def _hook(self, instruction: str, sequence: list[str]) -> bool:
# Send instruction to microcontroller to start hooking process
com.send(instruction)
# Record start time to respond to timeout
start = time.time()
# Check for timeout
pointer = 0
sequence_max = len(sequence) - 1
while time.time() - start < 5:
if ( decoder.decode_ascii( com.receive(1) ) ) == sequence[pointer]:
pointer += 1
else:
pointer = 0
if pointer == sequence_max:
return True
return False
def change_temperature(self, new_temps: list[float]) -> None:
pass
def _change_data(self, instruction: str, readback: list[str], data: list[float], readback_length: int) -> None:
# Hook to stream
if self._hook(instruction, readback):
while len(data) > 0:
if com.receive(readback_length) != '':
com.send_float(data.pop(0))
else:
com.close()
raise Exception('Failed to transmit data. No response from controller')
com.close()
else:
com.close()
raise ConnectionError('Failed to hook to controller data stream. No fitting response received')
def change_config(self, new_config: list[float]) -> None:
pass
self._change_data('PR', ['\n', 'P', 'R', '\n'], new_config, 3)
def change_temperature(self, temperatures: list[float]) -> None:
self._change_data('PT', ['\n', 'P', 'T', '\n'], temperatures, 3)
def enable_fastmode(self) -> None:
com.send('FM')
com.close()
def disable_fastmode(self) -> None:
com.send('NM')
com.close()