Simple serial monitor implementation
This commit is contained in:
BIN
assets/fonts/JetBrainsMono-Regular.ttf
Normal file
BIN
assets/fonts/JetBrainsMono-Regular.ttf
Normal file
Binary file not shown.
@@ -9,6 +9,7 @@ import dearpygui.dearpygui as dpg
|
|||||||
|
|
||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
import dataflux.config
|
import dataflux.config
|
||||||
|
from dataflux.tags import TEXT_SERIAL_CONSOLE
|
||||||
import dataflux.ui.windows
|
import dataflux.ui.windows
|
||||||
import dataflux.ui.worker
|
import dataflux.ui.worker
|
||||||
import dataflux.services.telemetry
|
import dataflux.services.telemetry
|
||||||
@@ -34,8 +35,7 @@ def _asset_path(relative_path: str) -> str:
|
|||||||
return str(candidate)
|
return str(candidate)
|
||||||
|
|
||||||
searched = ", ".join(str(candidate) for candidate in candidates)
|
searched = ", ".join(str(candidate) for candidate in candidates)
|
||||||
raise FileNotFoundError(
|
raise FileNotFoundError(f"Missing asset {relative_path!r}. Searched: {searched}")
|
||||||
f"Missing asset {relative_path!r}. Searched: {searched}")
|
|
||||||
|
|
||||||
|
|
||||||
def run() -> None:
|
def run() -> None:
|
||||||
@@ -61,8 +61,12 @@ def run() -> None:
|
|||||||
|
|
||||||
# Add Inter font to registry and bind as main app font
|
# Add Inter font to registry and bind as main app font
|
||||||
with dpg.font_registry():
|
with dpg.font_registry():
|
||||||
app_font = dpg.add_font(_asset_path(
|
app_font = dpg.add_font(_asset_path("assets/fonts/Inter-Regular.ttf"), 18 * 2)
|
||||||
"assets/fonts/Inter-Regular.ttf"), 18 * 2)
|
mono_font = dpg.add_font(
|
||||||
|
_asset_path("assets/fonts/JetBrainsMono-Regular.ttf"),
|
||||||
|
size=36,
|
||||||
|
label="mono_font",
|
||||||
|
)
|
||||||
dpg.bind_font(app_font)
|
dpg.bind_font(app_font)
|
||||||
|
|
||||||
dataflux.ui.windows.build_windows(state)
|
dataflux.ui.windows.build_windows(state)
|
||||||
@@ -74,6 +78,7 @@ def run() -> None:
|
|||||||
vp_h = dpg.get_viewport_client_height()
|
vp_h = dpg.get_viewport_client_height()
|
||||||
dpg.configure_item("main_window", pos=(0, 0), width=vp_w, height=vp_h)
|
dpg.configure_item("main_window", pos=(0, 0), width=vp_w, height=vp_h)
|
||||||
dpg.set_primary_window("main_window", True)
|
dpg.set_primary_window("main_window", True)
|
||||||
|
dpg.bind_item_font(TEXT_SERIAL_CONSOLE, mono_font)
|
||||||
|
|
||||||
state.ui_worker_thread = Thread(
|
state.ui_worker_thread = Thread(
|
||||||
target=dataflux.ui.worker.ui_worker, args=(state,), daemon=True
|
target=dataflux.ui.worker.ui_worker, args=(state,), daemon=True
|
||||||
@@ -82,8 +87,7 @@ def run() -> None:
|
|||||||
|
|
||||||
state.telemetry_thread_running = True
|
state.telemetry_thread_running = True
|
||||||
state.telemetry_thread = Thread(
|
state.telemetry_thread = Thread(
|
||||||
target=dataflux.services.telemetry.telemetry_worker, args=(
|
target=dataflux.services.telemetry.telemetry_worker, args=(state,), daemon=True
|
||||||
state,), daemon=True
|
|
||||||
)
|
)
|
||||||
state.telemetry_thread.start()
|
state.telemetry_thread.start()
|
||||||
|
|
||||||
|
|||||||
@@ -15,17 +15,28 @@ from dataflux.tags import (
|
|||||||
GRAPH_X_AXIS_SPEED,
|
GRAPH_X_AXIS_SPEED,
|
||||||
GRAPH_X_AXIS_TENG,
|
GRAPH_X_AXIS_TENG,
|
||||||
GRAPH_X_AXIS_VBAT,
|
GRAPH_X_AXIS_VBAT,
|
||||||
WINDOW_CONNECTION_MENU,
|
WINDOW_LORA_CONNECTION_MENU,
|
||||||
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def open_connection_window(sender, app_data, user_data) -> None:
|
def open_lora_connection_window(sender, app_data, user_data: AppState) -> None:
|
||||||
dataflux.ui.routines.windows.update_window_connection_menu_combo()
|
dataflux.ui.routines.windows.update_window_lora_connection_menu_combo(user_data)
|
||||||
dpg.show_item(WINDOW_CONNECTION_MENU)
|
dpg.show_item(WINDOW_LORA_CONNECTION_MENU)
|
||||||
|
|
||||||
|
|
||||||
def menu_file_disconnect(sender, app_data, user_data) -> None:
|
def open_serial_connection_window(sender, app_data, user_data: AppState) -> None:
|
||||||
|
dataflux.ui.routines.windows.update_window_serial_connection_menu_combo(user_data)
|
||||||
|
dpg.show_item(WINDOW_SERIAL_CONNECTION_MENU)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_io_disconnect_lora(sender, app_data, user_data: AppState) -> None:
|
||||||
|
dataflux.services.serial.disconnect_lora(user_data)
|
||||||
|
update_global_connection_status(user_data)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_io_disconnect_serial(sender, app_data, user_data: AppState) -> None:
|
||||||
dataflux.services.serial.disconnect_serial(user_data)
|
dataflux.services.serial.disconnect_serial(user_data)
|
||||||
update_global_connection_status(user_data)
|
update_global_connection_status(user_data)
|
||||||
|
|
||||||
|
|||||||
@@ -7,12 +7,23 @@ import dataflux.services.serial
|
|||||||
import dataflux.ui.routines
|
import dataflux.ui.routines
|
||||||
|
|
||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
from dataflux.tags import WINDOW_CONNECTION_MENU, WINDOW_CONNECTION_MENU_COMBO
|
from dataflux.tags import (
|
||||||
|
WINDOW_LORA_CONNECTION_MENU,
|
||||||
|
WINDOW_LORA_CONNECTION_MENU_COMBO,
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU,
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU_COMBO,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def connection_window_connect_lora(sender, app_data, user_data: AppState) -> None:
|
||||||
|
device = dpg.get_value(WINDOW_LORA_CONNECTION_MENU_COMBO)
|
||||||
|
dataflux.services.serial.connect_lora(user_data, device)
|
||||||
|
dataflux.ui.routines.update_global_connection_status(user_data)
|
||||||
|
dpg.hide_item(WINDOW_LORA_CONNECTION_MENU)
|
||||||
|
|
||||||
|
|
||||||
def connection_window_connect_serial(sender, app_data, user_data: AppState) -> None:
|
def connection_window_connect_serial(sender, app_data, user_data: AppState) -> None:
|
||||||
device = dpg.get_value(WINDOW_CONNECTION_MENU_COMBO)
|
device = dpg.get_value(WINDOW_SERIAL_CONNECTION_MENU_COMBO)
|
||||||
dataflux.services.serial.connect_serial(user_data, device)
|
dataflux.services.serial.connect_serial(user_data, device)
|
||||||
dataflux.ui.routines.update_global_connection_status(user_data)
|
dataflux.ui.routines.update_global_connection_status(user_data)
|
||||||
dpg.hide_item(WINDOW_CONNECTION_MENU)
|
dpg.hide_item(WINDOW_SERIAL_CONNECTION_MENU)
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,17 @@
|
|||||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
|
from concurrent.futures import thread
|
||||||
|
import os
|
||||||
from queue import Empty
|
from queue import Empty
|
||||||
from sys import base_exec_prefix
|
from sys import base_exec_prefix
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
|
import time
|
||||||
from serial import Serial
|
from serial import Serial
|
||||||
import serial.tools.list_ports
|
import serial.tools.list_ports
|
||||||
|
import dearpygui.dearpygui as dpg
|
||||||
from dataflux import telemetry_common
|
from dataflux import telemetry_common
|
||||||
|
from dataflux.tags import TEXT_SERIAL_CONSOLE
|
||||||
import dataflux.telemetry_common.telemetry_common
|
import dataflux.telemetry_common.telemetry_common
|
||||||
import dataflux.ui.routines.status
|
import dataflux.ui.routines.status
|
||||||
import dataflux.ui.routines
|
import dataflux.ui.routines
|
||||||
@@ -24,39 +29,96 @@ def list_serial_ports() -> list[str]:
|
|||||||
|
|
||||||
return valid_ports
|
return valid_ports
|
||||||
|
|
||||||
|
|
||||||
|
def connect_lora(state: AppState, device: str) -> None:
|
||||||
|
if state.lora_port is not None:
|
||||||
|
state.lora_port.close()
|
||||||
|
state.lora_port = None
|
||||||
|
|
||||||
|
state.lora_port = Serial(port=device, baudrate=115200)
|
||||||
|
state.lora_thread = Thread(target=lora_reader_worker, args=(state,), daemon=True)
|
||||||
|
state.lora_status_thread = Thread(
|
||||||
|
target=lora_status_worker, args=(state,), daemon=True
|
||||||
|
)
|
||||||
|
|
||||||
|
state.lora_thread_running = True
|
||||||
|
state.lora_status_thread.start()
|
||||||
|
state.lora_thread.start()
|
||||||
|
|
||||||
|
|
||||||
def connect_serial(state: AppState, device: str) -> None:
|
def connect_serial(state: AppState, device: str) -> None:
|
||||||
if state.serial_port is not None:
|
if state.serial_port is not None:
|
||||||
state.serial_port.close()
|
state.serial_port.close()
|
||||||
state.serial_port = None
|
state.serial_port = None
|
||||||
|
|
||||||
state.serial_port = Serial(port=device, baudrate=115200)
|
state.serial_port = Serial(port=device, baudrate=115200)
|
||||||
state.serial_thread = Thread(target=serial_reader_worker, args=(state,), daemon=True)
|
state.serial_thread = Thread(
|
||||||
state.serial_status_thread = Thread(target=serial_status_worker, args=(state,), daemon=True)
|
target=serial_reader_worker, args=(state,), daemon=True
|
||||||
|
)
|
||||||
|
state.serial_status_thread = Thread(
|
||||||
|
target=serial_status_worker, args=(state,), daemon=True
|
||||||
|
)
|
||||||
|
|
||||||
state.serial_thread_running = True
|
state.serial_thread_running = True
|
||||||
state.serial_status_thread.start()
|
state.serial_status_thread.start()
|
||||||
state.serial_thread.start()
|
state.serial_thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
def disconnect_lora(state: AppState) -> None:
|
||||||
|
if state.lora_port is not None:
|
||||||
|
state.lora_thread_running = False
|
||||||
|
state.lora_port.close()
|
||||||
|
state.lora_port = None
|
||||||
|
|
||||||
|
|
||||||
def disconnect_serial(state: AppState) -> None:
|
def disconnect_serial(state: AppState) -> None:
|
||||||
if state.serial_port is not None:
|
if state.serial_port is not None:
|
||||||
state.serial_thread_running = False
|
state.serial_thread_running = False
|
||||||
state.serial_port.close()
|
state.serial_port.close()
|
||||||
state.serial_port = None
|
state.serial_port = None
|
||||||
|
|
||||||
def serial_status_worker(state: AppState) -> None:
|
|
||||||
while state.serial_thread_running:
|
def lora_status_worker(state: AppState) -> None:
|
||||||
|
while state.lora_thread_running:
|
||||||
try:
|
try:
|
||||||
duration = state.serial_status_queue.get(timeout=0.1)
|
duration = state.lora_status_queue.get(timeout=0.1)
|
||||||
except Empty:
|
except Empty:
|
||||||
continue
|
continue
|
||||||
dataflux.ui.routines.status.flash_status_connection_status(duration)
|
dataflux.ui.routines.status.flash_status_connection_status(duration)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
def serial_reader_worker(state: AppState) -> None:
|
def serial_reader_worker(state: AppState) -> None:
|
||||||
while state.serial_thread_running:
|
while state.serial_thread_running:
|
||||||
port = state.serial_port
|
port = state.serial_port
|
||||||
|
if port is None:
|
||||||
|
break
|
||||||
|
if port.closed:
|
||||||
|
print("Port closed")
|
||||||
|
break
|
||||||
|
if port.port is not None and not os.path.exists(port.port):
|
||||||
|
break
|
||||||
|
|
||||||
|
line = port.readline()
|
||||||
|
|
||||||
|
if line:
|
||||||
|
text = line.decode("utf-8", errors="replace")
|
||||||
|
|
||||||
|
print(text, end="")
|
||||||
|
|
||||||
|
old = dpg.get_value(TEXT_SERIAL_CONSOLE)
|
||||||
|
dpg.set_value(TEXT_SERIAL_CONSOLE, old + text)
|
||||||
|
disconnect_serial(state)
|
||||||
|
dataflux.ui.routines.update_global_connection_status(state)
|
||||||
|
|
||||||
|
|
||||||
|
def serial_status_worker(state: AppState) -> None:
|
||||||
|
while state.serial_thread_running:
|
||||||
|
time.sleep(1)
|
||||||
|
|
||||||
|
|
||||||
|
def lora_reader_worker(state: AppState) -> None:
|
||||||
|
while state.lora_thread_running:
|
||||||
|
port = state.lora_port
|
||||||
if port is None:
|
if port is None:
|
||||||
break
|
break
|
||||||
if port.closed:
|
if port.closed:
|
||||||
@@ -71,12 +133,12 @@ def serial_reader_worker(state: AppState) -> None:
|
|||||||
parsed = parse_uart_packet(packet)
|
parsed = parse_uart_packet(packet)
|
||||||
if parsed is not None:
|
if parsed is not None:
|
||||||
state.packet_queue.put(parsed)
|
state.packet_queue.put(parsed)
|
||||||
state.serial_status_queue.put(0.1)
|
state.lora_status_queue.put(0.1)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Serial parser error: {e}")
|
print(f"Serial parser error: {e}")
|
||||||
break
|
break
|
||||||
disconnect_serial(state)
|
disconnect_lora(state)
|
||||||
dataflux.ui.routines.update_global_connection_status(state)
|
dataflux.ui.routines.update_global_connection_status(state)
|
||||||
|
|
||||||
|
|
||||||
@@ -107,15 +169,20 @@ def read_one_uart_packet(port: Serial) -> bytes | None:
|
|||||||
|
|
||||||
return body
|
return body
|
||||||
|
|
||||||
|
|
||||||
def parse_uart_packet(body: bytes) -> dict | None:
|
def parse_uart_packet(body: bytes) -> dict | None:
|
||||||
if len(body) < dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE:
|
if len(body) < dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
lora = dataflux.telemetry_common.telemetry_common.unpack_lora_header(body[:dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE])
|
lora = dataflux.telemetry_common.telemetry_common.unpack_lora_header(
|
||||||
|
body[: dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE]
|
||||||
|
)
|
||||||
payload = body[dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE :]
|
payload = body[dataflux.telemetry_common.telemetry_common.LORA_HEADER_SIZE :]
|
||||||
|
|
||||||
if lora.size != len(payload):
|
if lora.size != len(payload):
|
||||||
print(f"Serial size mismatch header says {lora.size} actual payload is {len(payload)}")
|
print(
|
||||||
|
f"Serial size mismatch header says {lora.size} actual payload is {len(payload)}"
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
calc_crc = dataflux.telemetry_common.telemetry_common.crc16_ccitt(payload)
|
calc_crc = dataflux.telemetry_common.telemetry_common.crc16_ccitt(payload)
|
||||||
@@ -135,7 +202,7 @@ def parse_uart_packet(body: bytes) -> dict | None:
|
|||||||
return {
|
return {
|
||||||
**base,
|
**base,
|
||||||
"type": "packet1",
|
"type": "packet1",
|
||||||
"ping": pkt.ping.decode("ascii", errors="replace")
|
"ping": pkt.ping.decode("ascii", errors="replace"),
|
||||||
}
|
}
|
||||||
|
|
||||||
if lora.version == 2:
|
if lora.version == 2:
|
||||||
@@ -153,5 +220,3 @@ def parse_uart_packet(body: bytes) -> dict | None:
|
|||||||
|
|
||||||
print("Unknown payload")
|
print("Unknown payload")
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ import csv
|
|||||||
|
|
||||||
def telemetry_worker(state: AppState):
|
def telemetry_worker(state: AppState):
|
||||||
while state.telemetry_thread_running:
|
while state.telemetry_thread_running:
|
||||||
if not state.serial_thread_running:
|
if not state.lora_thread_running:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ class Buffers:
|
|||||||
class AppState:
|
class AppState:
|
||||||
running: bool = True
|
running: bool = True
|
||||||
|
|
||||||
|
lora_port: Serial | None = None
|
||||||
|
lora_thread: Thread | None = None
|
||||||
|
lora_thread_running: bool = False
|
||||||
|
|
||||||
serial_port: Serial | None = None
|
serial_port: Serial | None = None
|
||||||
serial_thread: Thread | None = None
|
serial_thread: Thread | None = None
|
||||||
serial_thread_running: bool = False
|
serial_thread_running: bool = False
|
||||||
@@ -29,6 +33,9 @@ class AppState:
|
|||||||
telemetry_thread: Thread | None = None
|
telemetry_thread: Thread | None = None
|
||||||
telemetry_thread_running: bool = False
|
telemetry_thread_running: bool = False
|
||||||
|
|
||||||
|
lora_status_thread: Thread | None = None
|
||||||
|
lora_status_queue: Queue = field(default_factory=Queue)
|
||||||
|
|
||||||
serial_status_thread: Thread | None = None
|
serial_status_thread: Thread | None = None
|
||||||
serial_status_queue: Queue = field(default_factory=Queue)
|
serial_status_queue: Queue = field(default_factory=Queue)
|
||||||
|
|
||||||
|
|||||||
@@ -2,13 +2,19 @@
|
|||||||
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
# Copyright (C) 2026 Association Exergie <association.exergie@gmail.com>
|
||||||
# SPDX-License-Identifier: GPL-3.0-or-later
|
# SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
|
||||||
MENU_FILE_CONNECT: str = "menu_file_connect"
|
MENU_IO_CONNECT_LORA: str = "menu_io_connect_lora"
|
||||||
MENU_FILE_DISCONNECT: str = "menu_file_disconnect"
|
MENU_IO_CONNECT_SERIAL: str = "menu_io_connect_serial"
|
||||||
|
MENU_IO_DISCONNECT_LORA: str = "menu_io_disconnect_lora"
|
||||||
|
MENU_IO_DISCONNECT_SERIAL: str = "menu_io_disconnect_serial"
|
||||||
MENU_FILE_DUMP_BUFFERS: str = "menu_file_dump_buffers"
|
MENU_FILE_DUMP_BUFFERS: str = "menu_file_dump_buffers"
|
||||||
WINDOW_CONNECTION_MENU: str = "window_connection_menu"
|
WINDOW_LORA_CONNECTION_MENU: str = "window_lora_connection_menu"
|
||||||
WINDOW_CONNECTION_MENU_COMBO: str = "window_connection_menu_combo"
|
WINDOW_SERIAL_CONNECTION_MENU: str = "window_serial_connection_menu"
|
||||||
|
WINDOW_LORA_CONNECTION_MENU_COMBO: str = "window_lora_connection_menu_combo"
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU_COMBO: str = "window_serial_connection_menu_combo"
|
||||||
WINDOW_FILE_DIALOG_DUMP_BUFFERS: str = "window_file_dialog_dump_buffers"
|
WINDOW_FILE_DIALOG_DUMP_BUFFERS: str = "window_file_dialog_dump_buffers"
|
||||||
|
|
||||||
|
STATUS_LORA_STATUS_BOX: str = "status_lora_status_box"
|
||||||
|
STATUS_LORA_STATUS_TEXT: str = "status_lora_status_text"
|
||||||
STATUS_SERIAL_STATUS_BOX: str = "status_serial_status_box"
|
STATUS_SERIAL_STATUS_BOX: str = "status_serial_status_box"
|
||||||
STATUS_SERIAL_STATUS_TEXT: str = "status_serial_status_text"
|
STATUS_SERIAL_STATUS_TEXT: str = "status_serial_status_text"
|
||||||
|
|
||||||
@@ -20,6 +26,9 @@ LIVE_DATA_TENG_VALUE: str = "live_data_teng_value"
|
|||||||
|
|
||||||
PAGE_LIVE_DATA: str = "page_live_data"
|
PAGE_LIVE_DATA: str = "page_live_data"
|
||||||
PAGE_LAP_RECAP: str = "page_lap_recap"
|
PAGE_LAP_RECAP: str = "page_lap_recap"
|
||||||
|
PAGE_SERIAL_CONSOLE: str = "page_serial_console"
|
||||||
|
|
||||||
|
TEXT_SERIAL_CONSOLE: str = "text_serial_console"
|
||||||
|
|
||||||
SUB_PAGE_DATA_GRAPHS: str = "sub_page_data_graphs"
|
SUB_PAGE_DATA_GRAPHS: str = "sub_page_data_graphs"
|
||||||
SUB_PAGE_MAP: str = "sub_page_map"
|
SUB_PAGE_MAP: str = "sub_page_map"
|
||||||
|
|||||||
@@ -5,12 +5,25 @@
|
|||||||
import dearpygui.dearpygui as dpg
|
import dearpygui.dearpygui as dpg
|
||||||
|
|
||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
from dataflux.tags import MENU_FILE_CONNECT, MENU_FILE_DISCONNECT
|
from dataflux.tags import (
|
||||||
|
MENU_IO_CONNECT_LORA,
|
||||||
|
MENU_IO_CONNECT_SERIAL,
|
||||||
|
MENU_IO_DISCONNECT_LORA,
|
||||||
|
MENU_IO_DISCONNECT_SERIAL,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def update_menu_file_connection_status(state: AppState) -> None:
|
def update_menu_file_connection_status(state: AppState) -> None:
|
||||||
if state.serial_port is None:
|
if state.lora_port is None:
|
||||||
dpg.enable_item(MENU_FILE_CONNECT)
|
dpg.enable_item(MENU_IO_CONNECT_LORA)
|
||||||
dpg.disable_item(MENU_FILE_DISCONNECT)
|
dpg.disable_item(MENU_IO_DISCONNECT_LORA)
|
||||||
else:
|
else:
|
||||||
dpg.disable_item(MENU_FILE_CONNECT)
|
dpg.disable_item(MENU_IO_CONNECT_LORA)
|
||||||
dpg.enable_item(MENU_FILE_DISCONNECT)
|
dpg.enable_item(MENU_IO_DISCONNECT_LORA)
|
||||||
|
|
||||||
|
if state.serial_port is None:
|
||||||
|
dpg.enable_item(MENU_IO_CONNECT_SERIAL)
|
||||||
|
dpg.disable_item(MENU_IO_DISCONNECT_SERIAL)
|
||||||
|
else:
|
||||||
|
dpg.disable_item(MENU_IO_CONNECT_SERIAL)
|
||||||
|
dpg.enable_item(MENU_IO_DISCONNECT_SERIAL)
|
||||||
|
|||||||
@@ -4,10 +4,26 @@
|
|||||||
|
|
||||||
import dearpygui.dearpygui as dpg
|
import dearpygui.dearpygui as dpg
|
||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
from dataflux.tags import STATUS_SERIAL_STATUS_BOX, STATUS_SERIAL_STATUS_TEXT, THEME_STATUS_CONNECTED, THEME_STATUS_CONNECTED_BRIGHT, THEME_STATUS_DISCONNECTED
|
from dataflux.tags import (
|
||||||
|
STATUS_LORA_STATUS_BOX,
|
||||||
|
STATUS_LORA_STATUS_TEXT,
|
||||||
|
STATUS_SERIAL_STATUS_BOX,
|
||||||
|
STATUS_SERIAL_STATUS_TEXT,
|
||||||
|
THEME_STATUS_CONNECTED,
|
||||||
|
THEME_STATUS_CONNECTED_BRIGHT,
|
||||||
|
THEME_STATUS_DISCONNECTED,
|
||||||
|
)
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
def update_status_connection_status(state: AppState):
|
def update_status_connection_status(state: AppState):
|
||||||
|
if state.lora_port is None:
|
||||||
|
dpg.bind_item_theme(STATUS_LORA_STATUS_BOX, THEME_STATUS_DISCONNECTED)
|
||||||
|
dpg.set_value(STATUS_LORA_STATUS_TEXT, "LoRa: Disconnected")
|
||||||
|
else:
|
||||||
|
dpg.bind_item_theme(STATUS_LORA_STATUS_BOX, THEME_STATUS_CONNECTED)
|
||||||
|
dpg.set_value(STATUS_LORA_STATUS_TEXT, "LoRa: Connected")
|
||||||
|
|
||||||
if state.serial_port is None:
|
if state.serial_port is None:
|
||||||
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_DISCONNECTED)
|
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_DISCONNECTED)
|
||||||
dpg.set_value(STATUS_SERIAL_STATUS_TEXT, "Serial: Disconnected")
|
dpg.set_value(STATUS_SERIAL_STATUS_TEXT, "Serial: Disconnected")
|
||||||
@@ -15,7 +31,8 @@ def update_status_connection_status(state: AppState):
|
|||||||
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED)
|
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED)
|
||||||
dpg.set_value(STATUS_SERIAL_STATUS_TEXT, "Serial: Connected")
|
dpg.set_value(STATUS_SERIAL_STATUS_TEXT, "Serial: Connected")
|
||||||
|
|
||||||
|
|
||||||
def flash_status_connection_status(duration: float) -> None:
|
def flash_status_connection_status(duration: float) -> None:
|
||||||
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED_BRIGHT)
|
dpg.bind_item_theme(STATUS_LORA_STATUS_BOX, THEME_STATUS_CONNECTED_BRIGHT)
|
||||||
sleep(duration)
|
sleep(duration)
|
||||||
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_CONNECTED)
|
dpg.bind_item_theme(STATUS_LORA_STATUS_BOX, THEME_STATUS_CONNECTED)
|
||||||
|
|||||||
@@ -5,21 +5,47 @@ import dearpygui.dearpygui as dpg
|
|||||||
|
|
||||||
import dataflux.config
|
import dataflux.config
|
||||||
from dataflux.services.serial import list_serial_ports
|
from dataflux.services.serial import list_serial_ports
|
||||||
from dataflux.tags import PAGE_LAP_RECAP, PAGE_LIVE_DATA, SUB_PAGE_DATA_GRAPHS, SUB_PAGE_MAP, WINDOW_CONNECTION_MENU_COMBO
|
from dataflux.state import AppState
|
||||||
|
from dataflux.tags import (
|
||||||
|
PAGE_LAP_RECAP,
|
||||||
|
PAGE_LIVE_DATA,
|
||||||
|
PAGE_SERIAL_CONSOLE,
|
||||||
|
SUB_PAGE_DATA_GRAPHS,
|
||||||
|
SUB_PAGE_MAP,
|
||||||
|
WINDOW_LORA_CONNECTION_MENU_COMBO,
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU_COMBO,
|
||||||
|
)
|
||||||
|
|
||||||
def update_window_connection_menu_combo() -> None:
|
|
||||||
|
def update_window_lora_connection_menu_combo(state: AppState) -> None:
|
||||||
ports: list[str] = list_serial_ports()
|
ports: list[str] = list_serial_ports()
|
||||||
dpg.configure_item(WINDOW_CONNECTION_MENU_COMBO, items=ports)
|
if state.serial_port is not None and state.serial_thread_running:
|
||||||
|
port_name = state.serial_port.name
|
||||||
|
|
||||||
|
if port_name in ports:
|
||||||
|
ports.remove(port_name)
|
||||||
|
dpg.configure_item(WINDOW_LORA_CONNECTION_MENU_COMBO, items=ports)
|
||||||
|
|
||||||
|
|
||||||
|
def update_window_serial_connection_menu_combo(state: AppState) -> None:
|
||||||
|
ports: list[str] = list_serial_ports()
|
||||||
|
if state.lora_port is not None and state.lora_thread_running:
|
||||||
|
port_name = state.lora_port.name
|
||||||
|
|
||||||
|
if port_name in ports:
|
||||||
|
ports.remove(port_name)
|
||||||
|
dpg.configure_item(WINDOW_SERIAL_CONNECTION_MENU_COMBO, items=ports)
|
||||||
|
|
||||||
|
|
||||||
def hide_all_but(tag: str) -> None:
|
def hide_all_but(tag: str) -> None:
|
||||||
arr = [PAGE_LIVE_DATA, PAGE_LAP_RECAP]
|
arr = [PAGE_LIVE_DATA, PAGE_LAP_RECAP, PAGE_SERIAL_CONSOLE]
|
||||||
for item in arr:
|
for item in arr:
|
||||||
if tag == item:
|
if tag == item:
|
||||||
dpg.show_item(item)
|
dpg.show_item(item)
|
||||||
else:
|
else:
|
||||||
dpg.hide_item(item)
|
dpg.hide_item(item)
|
||||||
|
|
||||||
|
|
||||||
def toggle_window(tag: str) -> None:
|
def toggle_window(tag: str) -> None:
|
||||||
if tag == SUB_PAGE_DATA_GRAPHS:
|
if tag == SUB_PAGE_DATA_GRAPHS:
|
||||||
dpg.show_item(SUB_PAGE_DATA_GRAPHS)
|
dpg.show_item(SUB_PAGE_DATA_GRAPHS)
|
||||||
@@ -31,6 +57,3 @@ def toggle_window(tag: str) -> None:
|
|||||||
hide_all_but(PAGE_LIVE_DATA)
|
hide_all_but(PAGE_LIVE_DATA)
|
||||||
else:
|
else:
|
||||||
hide_all_but(tag)
|
hide_all_but(tag)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,21 +22,29 @@ from dataflux.tags import (
|
|||||||
LIVE_DATA_VBAT_VALUE,
|
LIVE_DATA_VBAT_VALUE,
|
||||||
LIVE_DATA_VEHICLE_TIME_VALUE,
|
LIVE_DATA_VEHICLE_TIME_VALUE,
|
||||||
LIVE_DATA_SPEED_VALUE,
|
LIVE_DATA_SPEED_VALUE,
|
||||||
MENU_FILE_CONNECT,
|
MENU_IO_CONNECT_LORA,
|
||||||
MENU_FILE_DISCONNECT,
|
MENU_IO_DISCONNECT_LORA,
|
||||||
MENU_FILE_DUMP_BUFFERS,
|
MENU_FILE_DUMP_BUFFERS,
|
||||||
|
MENU_IO_CONNECT_SERIAL,
|
||||||
|
MENU_IO_DISCONNECT_SERIAL,
|
||||||
PAGE_LAP_RECAP,
|
PAGE_LAP_RECAP,
|
||||||
PAGE_LIVE_DATA,
|
PAGE_LIVE_DATA,
|
||||||
|
PAGE_SERIAL_CONSOLE,
|
||||||
|
STATUS_LORA_STATUS_BOX,
|
||||||
|
STATUS_LORA_STATUS_TEXT,
|
||||||
STATUS_SERIAL_STATUS_BOX,
|
STATUS_SERIAL_STATUS_BOX,
|
||||||
STATUS_SERIAL_STATUS_TEXT,
|
STATUS_SERIAL_STATUS_TEXT,
|
||||||
SUB_PAGE_DATA_GRAPHS,
|
SUB_PAGE_DATA_GRAPHS,
|
||||||
SUB_PAGE_MAP,
|
SUB_PAGE_MAP,
|
||||||
|
TEXT_SERIAL_CONSOLE,
|
||||||
THEME_STATUS_CONNECTED,
|
THEME_STATUS_CONNECTED,
|
||||||
THEME_STATUS_CONNECTED_BRIGHT,
|
THEME_STATUS_CONNECTED_BRIGHT,
|
||||||
THEME_STATUS_DISCONNECTED,
|
THEME_STATUS_DISCONNECTED,
|
||||||
WINDOW_CONNECTION_MENU,
|
WINDOW_LORA_CONNECTION_MENU,
|
||||||
WINDOW_CONNECTION_MENU_COMBO,
|
WINDOW_LORA_CONNECTION_MENU_COMBO,
|
||||||
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU,
|
||||||
|
WINDOW_SERIAL_CONNECTION_MENU_COMBO,
|
||||||
)
|
)
|
||||||
from dataflux.ui.colors import STATUS_GREEN_BRIGHT, STATUS_GREEN_DARK, STATUS_RED_DARK
|
from dataflux.ui.colors import STATUS_GREEN_BRIGHT, STATUS_GREEN_DARK, STATUS_RED_DARK
|
||||||
|
|
||||||
@@ -57,19 +65,6 @@ def build_windows(state: AppState) -> None:
|
|||||||
dpg.set_global_font_scale(0.5)
|
dpg.set_global_font_scale(0.5)
|
||||||
with dpg.menu_bar():
|
with dpg.menu_bar():
|
||||||
with dpg.menu(label="File"):
|
with dpg.menu(label="File"):
|
||||||
dpg.add_menu_item(
|
|
||||||
label="Connect",
|
|
||||||
enabled=True,
|
|
||||||
tag=MENU_FILE_CONNECT,
|
|
||||||
callback=dataflux.callbacks.menu.open_connection_window,
|
|
||||||
)
|
|
||||||
dpg.add_menu_item(
|
|
||||||
label="Disonnect",
|
|
||||||
enabled=False,
|
|
||||||
tag=MENU_FILE_DISCONNECT,
|
|
||||||
callback=dataflux.callbacks.menu.menu_file_disconnect,
|
|
||||||
user_data=state,
|
|
||||||
)
|
|
||||||
dpg.add_menu_item(
|
dpg.add_menu_item(
|
||||||
label="Dump Buffers",
|
label="Dump Buffers",
|
||||||
enabled=True,
|
enabled=True,
|
||||||
@@ -77,6 +72,35 @@ def build_windows(state: AppState) -> None:
|
|||||||
callback=dataflux.callbacks.menu.menu_file_dump_buffers,
|
callback=dataflux.callbacks.menu.menu_file_dump_buffers,
|
||||||
)
|
)
|
||||||
dpg.add_menu_item(label="Quit")
|
dpg.add_menu_item(label="Quit")
|
||||||
|
with dpg.menu(label="IO"):
|
||||||
|
dpg.add_menu_item(
|
||||||
|
label="Connect LoRa",
|
||||||
|
enabled=True,
|
||||||
|
tag=MENU_IO_CONNECT_LORA,
|
||||||
|
callback=dataflux.callbacks.menu.open_lora_connection_window,
|
||||||
|
user_data=state,
|
||||||
|
)
|
||||||
|
dpg.add_menu_item(
|
||||||
|
label="Disonnect LoRa",
|
||||||
|
enabled=False,
|
||||||
|
tag=MENU_IO_DISCONNECT_LORA,
|
||||||
|
callback=dataflux.callbacks.menu.menu_io_disconnect_lora,
|
||||||
|
user_data=state,
|
||||||
|
)
|
||||||
|
dpg.add_menu_item(
|
||||||
|
label="Connect Serial",
|
||||||
|
enabled=True,
|
||||||
|
tag=MENU_IO_CONNECT_SERIAL,
|
||||||
|
callback=dataflux.callbacks.menu.open_serial_connection_window,
|
||||||
|
user_data=state,
|
||||||
|
)
|
||||||
|
dpg.add_menu_item(
|
||||||
|
label="Disonnect Serial",
|
||||||
|
enabled=False,
|
||||||
|
tag=MENU_IO_DISCONNECT_SERIAL,
|
||||||
|
callback=dataflux.callbacks.menu.menu_io_disconnect_serial,
|
||||||
|
user_data=state,
|
||||||
|
)
|
||||||
with dpg.menu(label="Window"):
|
with dpg.menu(label="Window"):
|
||||||
dpg.add_menu_item(
|
dpg.add_menu_item(
|
||||||
label="Live Graphs",
|
label="Live Graphs",
|
||||||
@@ -93,6 +117,11 @@ def build_windows(state: AppState) -> None:
|
|||||||
user_data=PAGE_LAP_RECAP,
|
user_data=PAGE_LAP_RECAP,
|
||||||
callback=dataflux.callbacks.menu.menu_window_select,
|
callback=dataflux.callbacks.menu.menu_window_select,
|
||||||
)
|
)
|
||||||
|
dpg.add_menu_item(
|
||||||
|
label="Serial Console",
|
||||||
|
user_data=PAGE_SERIAL_CONSOLE,
|
||||||
|
callback=dataflux.callbacks.menu.menu_window_select,
|
||||||
|
)
|
||||||
with dpg.menu(label="Data"):
|
with dpg.menu(label="Data"):
|
||||||
with dpg.menu(label="Timeframe"):
|
with dpg.menu(label="Timeframe"):
|
||||||
dpg.add_menu_item(
|
dpg.add_menu_item(
|
||||||
@@ -255,6 +284,18 @@ def build_windows(state: AppState) -> None:
|
|||||||
dpg.add_text("Lap Recap")
|
dpg.add_text("Lap Recap")
|
||||||
dpg.add_separator()
|
dpg.add_separator()
|
||||||
|
|
||||||
|
with dpg.group(tag=PAGE_SERIAL_CONSOLE, show=False):
|
||||||
|
with dpg.child_window(
|
||||||
|
width=-1,
|
||||||
|
height=-40,
|
||||||
|
border=True,
|
||||||
|
horizontal_scrollbar=False,
|
||||||
|
):
|
||||||
|
dpg.add_text(tag=TEXT_SERIAL_CONSOLE, wrap=0)
|
||||||
|
with dpg.group(horizontal=True):
|
||||||
|
dpg.add_input_text(width=-100)
|
||||||
|
dpg.add_button(label="Send", width=100)
|
||||||
|
|
||||||
with dpg.theme(tag=THEME_STATUS_CONNECTED):
|
with dpg.theme(tag=THEME_STATUS_CONNECTED):
|
||||||
with dpg.theme_component(dpg.mvChildWindow):
|
with dpg.theme_component(dpg.mvChildWindow):
|
||||||
dpg.add_theme_color(dpg.mvThemeCol_ChildBg, STATUS_GREEN_DARK)
|
dpg.add_theme_color(dpg.mvThemeCol_ChildBg, STATUS_GREEN_DARK)
|
||||||
@@ -275,6 +316,33 @@ def build_windows(state: AppState) -> None:
|
|||||||
no_scrollbar=True,
|
no_scrollbar=True,
|
||||||
):
|
):
|
||||||
with dpg.group(horizontal=True):
|
with dpg.group(horizontal=True):
|
||||||
|
with dpg.child_window(
|
||||||
|
width=200, height=28, border=False, tag=STATUS_LORA_STATUS_BOX
|
||||||
|
):
|
||||||
|
with dpg.table(
|
||||||
|
header_row=False,
|
||||||
|
resizable=False,
|
||||||
|
policy=dpg.mvTable_SizingStretchProp,
|
||||||
|
borders_innerV=False,
|
||||||
|
borders_innerH=False,
|
||||||
|
borders_outerH=False,
|
||||||
|
borders_outerV=False,
|
||||||
|
no_host_extendX=False,
|
||||||
|
no_pad_innerX=True,
|
||||||
|
):
|
||||||
|
dpg.add_table_column(init_width_or_weight=1.0)
|
||||||
|
dpg.add_table_column(width_fixed=True)
|
||||||
|
dpg.add_table_column(init_width_or_weight=1.0)
|
||||||
|
with dpg.table_row():
|
||||||
|
with dpg.table_cell():
|
||||||
|
pass
|
||||||
|
with dpg.table_cell():
|
||||||
|
dpg.add_text(
|
||||||
|
"LoRa: Disconnected",
|
||||||
|
tag=STATUS_LORA_STATUS_TEXT,
|
||||||
|
)
|
||||||
|
with dpg.table_cell():
|
||||||
|
pass
|
||||||
with dpg.child_window(
|
with dpg.child_window(
|
||||||
width=200, height=28, border=False, tag=STATUS_SERIAL_STATUS_BOX
|
width=200, height=28, border=False, tag=STATUS_SERIAL_STATUS_BOX
|
||||||
):
|
):
|
||||||
@@ -295,27 +363,43 @@ def build_windows(state: AppState) -> None:
|
|||||||
with dpg.table_row():
|
with dpg.table_row():
|
||||||
with dpg.table_cell():
|
with dpg.table_cell():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
with dpg.table_cell():
|
with dpg.table_cell():
|
||||||
dpg.add_text(
|
dpg.add_text(
|
||||||
"Serial: Disconnected",
|
"Serial: Disconnected",
|
||||||
tag=STATUS_SERIAL_STATUS_TEXT,
|
tag=STATUS_SERIAL_STATUS_TEXT,
|
||||||
)
|
)
|
||||||
|
|
||||||
with dpg.table_cell():
|
with dpg.table_cell():
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
dpg.bind_item_theme(STATUS_LORA_STATUS_BOX, THEME_STATUS_DISCONNECTED)
|
||||||
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_DISCONNECTED)
|
dpg.bind_item_theme(STATUS_SERIAL_STATUS_BOX, THEME_STATUS_DISCONNECTED)
|
||||||
|
|
||||||
with dpg.window(
|
with dpg.window(
|
||||||
label="Connection Menu",
|
label="LoRa Connection Menu",
|
||||||
tag=WINDOW_CONNECTION_MENU,
|
tag=WINDOW_LORA_CONNECTION_MENU,
|
||||||
show=False,
|
show=False,
|
||||||
modal=True,
|
modal=True,
|
||||||
no_collapse=True,
|
no_collapse=True,
|
||||||
width=300,
|
width=400,
|
||||||
|
no_resize=True,
|
||||||
):
|
):
|
||||||
dpg.add_combo([], tag=WINDOW_CONNECTION_MENU_COMBO)
|
dpg.add_combo([], tag=WINDOW_LORA_CONNECTION_MENU_COMBO)
|
||||||
|
dpg.add_button(
|
||||||
|
label="Connect",
|
||||||
|
callback=dataflux.callbacks.serial.connection_window_connect_lora,
|
||||||
|
user_data=state,
|
||||||
|
)
|
||||||
|
|
||||||
|
with dpg.window(
|
||||||
|
label="Serial Connection Menu",
|
||||||
|
tag=WINDOW_SERIAL_CONNECTION_MENU,
|
||||||
|
show=False,
|
||||||
|
modal=True,
|
||||||
|
no_collapse=True,
|
||||||
|
width=400,
|
||||||
|
no_resize=True,
|
||||||
|
):
|
||||||
|
dpg.add_combo([], tag=WINDOW_SERIAL_CONNECTION_MENU_COMBO)
|
||||||
dpg.add_button(
|
dpg.add_button(
|
||||||
label="Connect",
|
label="Connect",
|
||||||
callback=dataflux.callbacks.serial.connection_window_connect_serial,
|
callback=dataflux.callbacks.serial.connection_window_connect_serial,
|
||||||
|
|||||||
@@ -8,7 +8,17 @@ import time
|
|||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
|
||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
from dataflux.tags import GRAPH_SERIES_SPEED, GRAPH_SERIES_TENG, GRAPH_SERIES_VBAT, GRAPH_X_AXIS_SPEED, LIVE_DATA_TENG_VALUE, LIVE_DATA_UTC_TIME_VALUE, LIVE_DATA_VBAT_VALUE, LIVE_DATA_VEHICLE_TIME_VALUE, LIVE_DATA_SPEED_VALUE
|
from dataflux.tags import (
|
||||||
|
GRAPH_SERIES_SPEED,
|
||||||
|
GRAPH_SERIES_TENG,
|
||||||
|
GRAPH_SERIES_VBAT,
|
||||||
|
GRAPH_X_AXIS_SPEED,
|
||||||
|
LIVE_DATA_TENG_VALUE,
|
||||||
|
LIVE_DATA_UTC_TIME_VALUE,
|
||||||
|
LIVE_DATA_VBAT_VALUE,
|
||||||
|
LIVE_DATA_VEHICLE_TIME_VALUE,
|
||||||
|
LIVE_DATA_SPEED_VALUE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def ui_worker(state: AppState):
|
def ui_worker(state: AppState):
|
||||||
@@ -26,7 +36,7 @@ def ui_worker(state: AppState):
|
|||||||
dpg.set_value(LIVE_DATA_UTC_TIME_VALUE, formatted)
|
dpg.set_value(LIVE_DATA_UTC_TIME_VALUE, formatted)
|
||||||
last_datetime = formatted
|
last_datetime = formatted
|
||||||
|
|
||||||
if state.serial_thread_running and state.telemetry_valid:
|
if state.lora_thread_running and state.telemetry_valid:
|
||||||
x_common: list[float] | None = None
|
x_common: list[float] | None = None
|
||||||
speed_y: list[float] | None = None
|
speed_y: list[float] | None = None
|
||||||
vbat_y: list[float] | None = None
|
vbat_y: list[float] | None = None
|
||||||
@@ -59,7 +69,6 @@ def ui_worker(state: AppState):
|
|||||||
dpg.set_value(LIVE_DATA_SPEED_VALUE, formatted)
|
dpg.set_value(LIVE_DATA_SPEED_VALUE, formatted)
|
||||||
last_veh_speed = formatted
|
last_veh_speed = formatted
|
||||||
|
|
||||||
|
|
||||||
# VBAT
|
# VBAT
|
||||||
formatted = f"{vbat:05.2f}"
|
formatted = f"{vbat:05.2f}"
|
||||||
if formatted != last_vbat:
|
if formatted != last_vbat:
|
||||||
@@ -87,7 +96,3 @@ def ui_worker(state: AppState):
|
|||||||
no_data_written = True
|
no_data_written = True
|
||||||
|
|
||||||
time.sleep(0.05)
|
time.sleep(0.05)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user