Added lap recap graphs and loading
This commit is contained in:
@@ -2,6 +2,7 @@
|
|||||||
# 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 ast import arg
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
import dearpygui.dearpygui as dpg
|
import dearpygui.dearpygui as dpg
|
||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
@@ -16,6 +17,7 @@ from dataflux.tags import (
|
|||||||
GRAPH_X_AXIS_TENG,
|
GRAPH_X_AXIS_TENG,
|
||||||
GRAPH_X_AXIS_VBAT,
|
GRAPH_X_AXIS_VBAT,
|
||||||
WINDOW_FILE_DIALOG_AUTOSAVE_BUFFERS,
|
WINDOW_FILE_DIALOG_AUTOSAVE_BUFFERS,
|
||||||
|
WINDOW_FILE_DIALOG_LOAD_LAP,
|
||||||
WINDOW_LORA_CONNECTION_MENU,
|
WINDOW_LORA_CONNECTION_MENU,
|
||||||
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
||||||
WINDOW_SERIAL_CONNECTION_MENU,
|
WINDOW_SERIAL_CONNECTION_MENU,
|
||||||
@@ -28,11 +30,8 @@ def open_lora_connection_window(sender, app_data, user_data: AppState) -> None:
|
|||||||
|
|
||||||
|
|
||||||
def open_serial_connection_window(sender, app_data, user_data: AppState) -> None:
|
def open_serial_connection_window(sender, app_data, user_data: AppState) -> None:
|
||||||
print("Handling serial window open callback")
|
|
||||||
dataflux.ui.routines.windows.update_window_serial_connection_menu_combo(user_data)
|
dataflux.ui.routines.windows.update_window_serial_connection_menu_combo(user_data)
|
||||||
print("Combo updated")
|
|
||||||
dpg.show_item(WINDOW_SERIAL_CONNECTION_MENU)
|
dpg.show_item(WINDOW_SERIAL_CONNECTION_MENU)
|
||||||
print("Window shown")
|
|
||||||
|
|
||||||
|
|
||||||
def menu_io_disconnect_lora(sender, app_data, user_data: AppState) -> None:
|
def menu_io_disconnect_lora(sender, app_data, user_data: AppState) -> None:
|
||||||
@@ -49,6 +48,10 @@ def menu_file_dump_buffers(sender, app_data, user_data: AppState) -> None:
|
|||||||
dpg.show_item(WINDOW_FILE_DIALOG_DUMP_BUFFERS)
|
dpg.show_item(WINDOW_FILE_DIALOG_DUMP_BUFFERS)
|
||||||
|
|
||||||
|
|
||||||
|
def menu_file_load_lap(sender, app_data, user_data: AppState) -> None:
|
||||||
|
dpg.show_item(WINDOW_FILE_DIALOG_LOAD_LAP)
|
||||||
|
|
||||||
|
|
||||||
def menu_file_quit(sender, app_data, user_data) -> None:
|
def menu_file_quit(sender, app_data, user_data) -> None:
|
||||||
dpg.stop_dearpygui()
|
dpg.stop_dearpygui()
|
||||||
|
|
||||||
@@ -82,6 +85,15 @@ def window_file_dialog_autosave_buffers_ok(
|
|||||||
user_data.autosave_buffer_thread.start()
|
user_data.autosave_buffer_thread.start()
|
||||||
|
|
||||||
|
|
||||||
|
def window_file_dialog_load_lap_ok(sender, app_data, user_data: AppState) -> None:
|
||||||
|
user_data.lap_loader_thread = Thread(
|
||||||
|
target=dataflux.services.telemetry.lap_load_worker,
|
||||||
|
args=(user_data, app_data["file_path_name"]),
|
||||||
|
daemon=True,
|
||||||
|
)
|
||||||
|
user_data.lap_loader_thread.start()
|
||||||
|
|
||||||
|
|
||||||
def menu_window_select(sender, app_data, user_data: str) -> None:
|
def menu_window_select(sender, app_data, user_data: str) -> None:
|
||||||
dataflux.ui.routines.windows.toggle_window(user_data)
|
dataflux.ui.routines.windows.toggle_window(user_data)
|
||||||
|
|
||||||
|
|||||||
@@ -72,14 +72,20 @@ def connect_serial(state: AppState, device: str) -> None:
|
|||||||
def disconnect_lora(state: AppState) -> None:
|
def disconnect_lora(state: AppState) -> None:
|
||||||
if state.lora_port is not None:
|
if state.lora_port is not None:
|
||||||
state.lora_thread_running = False
|
state.lora_thread_running = False
|
||||||
state.lora_port.close()
|
try:
|
||||||
|
state.lora_port.close()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
state.lora_port = None
|
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()
|
try:
|
||||||
|
state.serial_port.close()
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
state.serial_port = None
|
state.serial_port = None
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +111,14 @@ def serial_worker(state: AppState) -> None:
|
|||||||
if port.port is not None and not os.path.exists(port.port):
|
if port.port is not None and not os.path.exists(port.port):
|
||||||
break
|
break
|
||||||
|
|
||||||
line = port.readline()
|
try:
|
||||||
|
line = port.readline()
|
||||||
|
except TypeError:
|
||||||
|
break
|
||||||
|
except serial.SerialException:
|
||||||
|
break
|
||||||
|
except OSError:
|
||||||
|
break
|
||||||
|
|
||||||
if line:
|
if line:
|
||||||
text = line.decode("utf-8", errors="replace")
|
text = line.decode("utf-8", errors="replace")
|
||||||
@@ -155,8 +168,7 @@ def lora_reader_worker(state: AppState) -> None:
|
|||||||
state.packet_queue.put(parsed)
|
state.packet_queue.put(parsed)
|
||||||
state.lora_status_queue.put(0.1)
|
state.lora_status_queue.put(0.1)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception:
|
||||||
print(f"Serial parser error: {e}")
|
|
||||||
break
|
break
|
||||||
disconnect_lora(state)
|
disconnect_lora(state)
|
||||||
dataflux.ui.routines.update_global_connection_status(state)
|
dataflux.ui.routines.update_global_connection_status(state)
|
||||||
|
|||||||
@@ -184,6 +184,40 @@ def autosave_worker(state: AppState, path: str) -> None:
|
|||||||
time.sleep(30)
|
time.sleep(30)
|
||||||
|
|
||||||
|
|
||||||
|
def lap_load_worker(state: AppState, path: str) -> None:
|
||||||
|
state.lap_recap_buffers.timestamp.clear()
|
||||||
|
state.lap_recap_buffers.speed.clear()
|
||||||
|
state.lap_recap_buffers.vbat.clear()
|
||||||
|
state.lap_recap_buffers.teng.clear()
|
||||||
|
state.lap_recap_buffers.lat.clear()
|
||||||
|
state.lap_recap_buffers.lng.clear()
|
||||||
|
|
||||||
|
load_path = Path(path)
|
||||||
|
|
||||||
|
try:
|
||||||
|
with load_path.open("r", newline="", encoding="utf-8") as f:
|
||||||
|
reader = csv.DictReader(f)
|
||||||
|
|
||||||
|
for row in reader:
|
||||||
|
state.lap_recap_buffers.timestamp.append(int(row["timestamp"]))
|
||||||
|
state.lap_recap_buffers.speed.append(float(row["speed"]))
|
||||||
|
state.lap_recap_buffers.vbat.append(float(row["vbat"]))
|
||||||
|
state.lap_recap_buffers.teng.append(float(row["teng"]))
|
||||||
|
state.lap_recap_buffers.lat.append(float(row["lat"]))
|
||||||
|
state.lap_recap_buffers.lng.append(float(row["lng"]))
|
||||||
|
|
||||||
|
except FileNotFoundError:
|
||||||
|
pass
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
except ValueError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
state.lap_recap_updated = True
|
||||||
|
|
||||||
|
state.lap_loader_thread = None
|
||||||
|
|
||||||
|
|
||||||
def closest_idx(values: list[int], target: int) -> int:
|
def closest_idx(values: list[int], target: int) -> int:
|
||||||
if not values:
|
if not values:
|
||||||
raise ValueError("Cannot find closest index in an empty list")
|
raise ValueError("Cannot find closest index in an empty list")
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ class AppState:
|
|||||||
live_buffers_updated: bool = False
|
live_buffers_updated: bool = False
|
||||||
live_buffer_len: int = 30
|
live_buffer_len: int = 30
|
||||||
|
|
||||||
|
lap_recap_buffers: Buffers = field(default_factory=Buffers)
|
||||||
|
lap_recap_updated: bool = False
|
||||||
|
|
||||||
lap_lock: Lock = field(default_factory=Lock)
|
lap_lock: Lock = field(default_factory=Lock)
|
||||||
new_laps: Queue = field(default_factory=Queue)
|
new_laps: Queue = field(default_factory=Queue)
|
||||||
laps: list[LapInfo] = field(default_factory=list)
|
laps: list[LapInfo] = field(default_factory=list)
|
||||||
@@ -77,4 +80,6 @@ class AppState:
|
|||||||
autosave_enabled: bool = False
|
autosave_enabled: bool = False
|
||||||
autosave_path: Path | None = None
|
autosave_path: Path | None = None
|
||||||
|
|
||||||
|
lap_loader_thread: Thread | None = None
|
||||||
|
|
||||||
lock: Lock = field(default_factory=Lock)
|
lock: Lock = field(default_factory=Lock)
|
||||||
|
|||||||
@@ -8,12 +8,14 @@ MENU_IO_DISCONNECT_LORA: str = "menu_io_disconnect_lora"
|
|||||||
MENU_IO_DISCONNECT_SERIAL: str = "menu_io_disconnect_serial"
|
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"
|
||||||
MENU_FILE_AUTOSAVE_BUFFERS: str = "menu_file_autosave_buffers"
|
MENU_FILE_AUTOSAVE_BUFFERS: str = "menu_file_autosave_buffers"
|
||||||
|
MENU_FILE_LOAD_LAP: str = "menu_file_load_lap"
|
||||||
WINDOW_LORA_CONNECTION_MENU: str = "window_lora_connection_menu"
|
WINDOW_LORA_CONNECTION_MENU: str = "window_lora_connection_menu"
|
||||||
WINDOW_SERIAL_CONNECTION_MENU: str = "window_serial_connection_menu"
|
WINDOW_SERIAL_CONNECTION_MENU: str = "window_serial_connection_menu"
|
||||||
WINDOW_LORA_CONNECTION_MENU_COMBO: str = "window_lora_connection_menu_combo"
|
WINDOW_LORA_CONNECTION_MENU_COMBO: str = "window_lora_connection_menu_combo"
|
||||||
WINDOW_SERIAL_CONNECTION_MENU_COMBO: str = "window_serial_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"
|
||||||
WINDOW_FILE_DIALOG_AUTOSAVE_BUFFERS: str = "window_file_dialog_autosave_buffers"
|
WINDOW_FILE_DIALOG_AUTOSAVE_BUFFERS: str = "window_file_dialog_autosave_buffers"
|
||||||
|
WINDOW_FILE_DIALOG_LOAD_LAP: str = "window_file_dialog_load_lap"
|
||||||
|
|
||||||
CHILD_WINDOW_SERIAL_CONSOLE: str = "child_window_serial_console"
|
CHILD_WINDOW_SERIAL_CONSOLE: str = "child_window_serial_console"
|
||||||
|
|
||||||
@@ -49,10 +51,22 @@ GRAPH_X_AXIS_SPEED: str = "graph_x_axis_speed"
|
|||||||
GRAPH_Y_AXIS_SPEED: str = "graph_y_axis_speed"
|
GRAPH_Y_AXIS_SPEED: str = "graph_y_axis_speed"
|
||||||
GRAPH_SERIES_SPEED: str = "graph_series_speed"
|
GRAPH_SERIES_SPEED: str = "graph_series_speed"
|
||||||
|
|
||||||
|
GRAPH_X_AXIS_SPEED_LR: str = "graph_x_axis_speed_lr"
|
||||||
|
GRAPH_Y_AXIS_SPEED_LR: str = "graph_y_axis_speed_lr"
|
||||||
|
GRAPH_SERIES_SPEED_LR: str = "graph_series_speed_lr"
|
||||||
|
|
||||||
GRAPH_X_AXIS_VBAT: str = "graph_x_axis_vbat"
|
GRAPH_X_AXIS_VBAT: str = "graph_x_axis_vbat"
|
||||||
GRAPH_Y_AXIS_VBAT: str = "graph_y_axis_vbat"
|
GRAPH_Y_AXIS_VBAT: str = "graph_y_axis_vbat"
|
||||||
GRAPH_SERIES_VBAT: str = "graph_series_vbat"
|
GRAPH_SERIES_VBAT: str = "graph_series_vbat"
|
||||||
|
|
||||||
|
GRAPH_X_AXIS_VBAT_LR: str = "graph_x_axis_vbat_lr"
|
||||||
|
GRAPH_Y_AXIS_VBAT_LR: str = "graph_y_axis_vbat_lr"
|
||||||
|
GRAPH_SERIES_VBAT_LR: str = "graph_series_vbat_lr"
|
||||||
|
|
||||||
GRAPH_X_AXIS_TENG: str = "graph_x_axis_teng"
|
GRAPH_X_AXIS_TENG: str = "graph_x_axis_teng"
|
||||||
GRAPH_Y_AXIS_TENG: str = "graph_y_axis_teng"
|
GRAPH_Y_AXIS_TENG: str = "graph_y_axis_teng"
|
||||||
GRAPH_SERIES_TENG: str = "graph_series_teng"
|
GRAPH_SERIES_TENG: str = "graph_series_teng"
|
||||||
|
|
||||||
|
GRAPH_X_AXIS_TENG_LR: str = "graph_x_axis_teng_lr"
|
||||||
|
GRAPH_Y_AXIS_TENG_LR: str = "graph_y_axis_teng_lr"
|
||||||
|
GRAPH_SERIES_TENG_LR: str = "graph_series_teng_lr"
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ def update_window_lora_connection_menu_combo(state: AppState) -> None:
|
|||||||
if port_name in ports:
|
if port_name in ports:
|
||||||
ports.remove(port_name)
|
ports.remove(port_name)
|
||||||
dpg.configure_item(WINDOW_LORA_CONNECTION_MENU_COMBO, items=ports)
|
dpg.configure_item(WINDOW_LORA_CONNECTION_MENU_COMBO, items=ports)
|
||||||
|
dpg.set_value(WINDOW_LORA_CONNECTION_MENU_COMBO, "")
|
||||||
|
|
||||||
|
|
||||||
def update_window_serial_connection_menu_combo(state: AppState) -> None:
|
def update_window_serial_connection_menu_combo(state: AppState) -> None:
|
||||||
@@ -34,6 +35,7 @@ def update_window_serial_connection_menu_combo(state: AppState) -> None:
|
|||||||
if port_name in ports:
|
if port_name in ports:
|
||||||
ports.remove(port_name)
|
ports.remove(port_name)
|
||||||
dpg.configure_item(WINDOW_SERIAL_CONNECTION_MENU_COMBO, items=ports)
|
dpg.configure_item(WINDOW_SERIAL_CONNECTION_MENU_COMBO, items=ports)
|
||||||
|
dpg.set_value(WINDOW_SERIAL_CONNECTION_MENU_COMBO, "")
|
||||||
|
|
||||||
|
|
||||||
def hide_all_but(tag: str) -> None:
|
def hide_all_but(tag: str) -> None:
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
# 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 operator import call
|
||||||
import dearpygui.dearpygui as dpg
|
import dearpygui.dearpygui as dpg
|
||||||
import dataflux.callbacks.menu
|
import dataflux.callbacks.menu
|
||||||
import dataflux.callbacks.serial
|
import dataflux.callbacks.serial
|
||||||
@@ -11,14 +12,23 @@ from dataflux.tags import (
|
|||||||
BUTTON_SERIAL_CONSOLE_SEND,
|
BUTTON_SERIAL_CONSOLE_SEND,
|
||||||
CHILD_WINDOW_SERIAL_CONSOLE,
|
CHILD_WINDOW_SERIAL_CONSOLE,
|
||||||
GRAPH_SERIES_SPEED,
|
GRAPH_SERIES_SPEED,
|
||||||
|
GRAPH_SERIES_SPEED_LR,
|
||||||
GRAPH_SERIES_TENG,
|
GRAPH_SERIES_TENG,
|
||||||
|
GRAPH_SERIES_TENG_LR,
|
||||||
GRAPH_SERIES_VBAT,
|
GRAPH_SERIES_VBAT,
|
||||||
|
GRAPH_SERIES_VBAT_LR,
|
||||||
GRAPH_X_AXIS_SPEED,
|
GRAPH_X_AXIS_SPEED,
|
||||||
|
GRAPH_X_AXIS_SPEED_LR,
|
||||||
GRAPH_X_AXIS_TENG,
|
GRAPH_X_AXIS_TENG,
|
||||||
|
GRAPH_X_AXIS_TENG_LR,
|
||||||
GRAPH_X_AXIS_VBAT,
|
GRAPH_X_AXIS_VBAT,
|
||||||
|
GRAPH_X_AXIS_VBAT_LR,
|
||||||
GRAPH_Y_AXIS_SPEED,
|
GRAPH_Y_AXIS_SPEED,
|
||||||
|
GRAPH_Y_AXIS_SPEED_LR,
|
||||||
GRAPH_Y_AXIS_TENG,
|
GRAPH_Y_AXIS_TENG,
|
||||||
|
GRAPH_Y_AXIS_TENG_LR,
|
||||||
GRAPH_Y_AXIS_VBAT,
|
GRAPH_Y_AXIS_VBAT,
|
||||||
|
GRAPH_Y_AXIS_VBAT_LR,
|
||||||
INPUT_SERIAL_CONSOLE,
|
INPUT_SERIAL_CONSOLE,
|
||||||
LIVE_DATA_TENG_VALUE,
|
LIVE_DATA_TENG_VALUE,
|
||||||
LIVE_DATA_UTC_TIME_VALUE,
|
LIVE_DATA_UTC_TIME_VALUE,
|
||||||
@@ -26,6 +36,7 @@ from dataflux.tags import (
|
|||||||
LIVE_DATA_VEHICLE_TIME_VALUE,
|
LIVE_DATA_VEHICLE_TIME_VALUE,
|
||||||
LIVE_DATA_SPEED_VALUE,
|
LIVE_DATA_SPEED_VALUE,
|
||||||
MENU_FILE_AUTOSAVE_BUFFERS,
|
MENU_FILE_AUTOSAVE_BUFFERS,
|
||||||
|
MENU_FILE_LOAD_LAP,
|
||||||
MENU_IO_CONNECT_LORA,
|
MENU_IO_CONNECT_LORA,
|
||||||
MENU_IO_DISCONNECT_LORA,
|
MENU_IO_DISCONNECT_LORA,
|
||||||
MENU_FILE_DUMP_BUFFERS,
|
MENU_FILE_DUMP_BUFFERS,
|
||||||
@@ -45,6 +56,7 @@ from dataflux.tags import (
|
|||||||
THEME_STATUS_CONNECTED_BRIGHT,
|
THEME_STATUS_CONNECTED_BRIGHT,
|
||||||
THEME_STATUS_DISCONNECTED,
|
THEME_STATUS_DISCONNECTED,
|
||||||
WINDOW_FILE_DIALOG_AUTOSAVE_BUFFERS,
|
WINDOW_FILE_DIALOG_AUTOSAVE_BUFFERS,
|
||||||
|
WINDOW_FILE_DIALOG_LOAD_LAP,
|
||||||
WINDOW_LORA_CONNECTION_MENU,
|
WINDOW_LORA_CONNECTION_MENU,
|
||||||
WINDOW_LORA_CONNECTION_MENU_COMBO,
|
WINDOW_LORA_CONNECTION_MENU_COMBO,
|
||||||
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
WINDOW_FILE_DIALOG_DUMP_BUFFERS,
|
||||||
@@ -85,6 +97,12 @@ def build_windows(state: AppState) -> None:
|
|||||||
callback=dataflux.callbacks.menu.menu_file_autosave_buffers,
|
callback=dataflux.callbacks.menu.menu_file_autosave_buffers,
|
||||||
user_data=state,
|
user_data=state,
|
||||||
)
|
)
|
||||||
|
dpg.add_menu_item(
|
||||||
|
label="Load Lap",
|
||||||
|
enabled=True,
|
||||||
|
tag=MENU_FILE_LOAD_LAP,
|
||||||
|
callback=dataflux.callbacks.menu.menu_file_load_lap,
|
||||||
|
)
|
||||||
dpg.add_menu_item(
|
dpg.add_menu_item(
|
||||||
label="Quit", callback=dataflux.callbacks.menu.menu_file_quit
|
label="Quit", callback=dataflux.callbacks.menu.menu_file_quit
|
||||||
)
|
)
|
||||||
@@ -300,6 +318,53 @@ def build_windows(state: AppState) -> None:
|
|||||||
dpg.add_text("Lap Recap")
|
dpg.add_text("Lap Recap")
|
||||||
dpg.add_separator()
|
dpg.add_separator()
|
||||||
|
|
||||||
|
with dpg.plot(label="Speed", height=250, width=-1, no_inputs=True):
|
||||||
|
dpg.add_plot_legend()
|
||||||
|
dpg.add_plot_axis(
|
||||||
|
dpg.mvXAxis, label="Time", tag=GRAPH_X_AXIS_SPEED_LR
|
||||||
|
)
|
||||||
|
y_axis_speed = dpg.add_plot_axis(
|
||||||
|
dpg.mvYAxis, label="Speed", tag=GRAPH_Y_AXIS_SPEED_LR
|
||||||
|
)
|
||||||
|
dpg.set_axis_limits(GRAPH_Y_AXIS_SPEED_LR, ymin=0, ymax=50)
|
||||||
|
dpg.set_axis_limits(GRAPH_X_AXIS_SPEED_LR, ymin=-30, ymax=0)
|
||||||
|
dpg.add_line_series(
|
||||||
|
[], [], parent=y_axis_speed, tag=GRAPH_SERIES_SPEED_LR
|
||||||
|
)
|
||||||
|
with dpg.plot(
|
||||||
|
label="Battery Voltage",
|
||||||
|
height=250,
|
||||||
|
width=-1,
|
||||||
|
no_inputs=True,
|
||||||
|
):
|
||||||
|
dpg.add_plot_legend()
|
||||||
|
dpg.add_plot_axis(
|
||||||
|
dpg.mvXAxis, label="Time", tag=GRAPH_X_AXIS_VBAT_LR
|
||||||
|
)
|
||||||
|
y_axis_vbat = dpg.add_plot_axis(
|
||||||
|
dpg.mvYAxis, label="Voltage", tag=GRAPH_Y_AXIS_VBAT_LR
|
||||||
|
)
|
||||||
|
dpg.set_axis_limits(GRAPH_Y_AXIS_VBAT_LR, ymin=0, ymax=20)
|
||||||
|
dpg.set_axis_limits(GRAPH_X_AXIS_VBAT_LR, ymin=-30, ymax=0)
|
||||||
|
dpg.add_line_series(
|
||||||
|
[], [], parent=y_axis_vbat, tag=GRAPH_SERIES_VBAT_LR
|
||||||
|
)
|
||||||
|
with dpg.plot(
|
||||||
|
label="Engine Temp", height=250, width=-1, no_inputs=True
|
||||||
|
):
|
||||||
|
dpg.add_plot_legend()
|
||||||
|
dpg.add_plot_axis(
|
||||||
|
dpg.mvXAxis, label="Time", tag=GRAPH_X_AXIS_TENG_LR
|
||||||
|
)
|
||||||
|
y_axis_teng = dpg.add_plot_axis(
|
||||||
|
dpg.mvYAxis, label="Temperature", tag=GRAPH_Y_AXIS_TENG_LR
|
||||||
|
)
|
||||||
|
dpg.set_axis_limits(GRAPH_Y_AXIS_TENG_LR, ymin=0, ymax=120)
|
||||||
|
dpg.set_axis_limits(GRAPH_X_AXIS_TENG_LR, ymin=-30, ymax=0)
|
||||||
|
dpg.add_line_series(
|
||||||
|
[], [], parent=y_axis_teng, tag=GRAPH_SERIES_TENG_LR
|
||||||
|
)
|
||||||
|
|
||||||
with dpg.group(tag=PAGE_SERIAL_CONSOLE, show=False):
|
with dpg.group(tag=PAGE_SERIAL_CONSOLE, show=False):
|
||||||
with dpg.child_window(
|
with dpg.child_window(
|
||||||
tag=CHILD_WINDOW_SERIAL_CONSOLE,
|
tag=CHILD_WINDOW_SERIAL_CONSOLE,
|
||||||
@@ -452,3 +517,15 @@ def build_windows(state: AppState) -> None:
|
|||||||
user_data=state,
|
user_data=state,
|
||||||
):
|
):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
with dpg.file_dialog(
|
||||||
|
directory_selector=False,
|
||||||
|
show=False,
|
||||||
|
tag=WINDOW_FILE_DIALOG_LOAD_LAP,
|
||||||
|
width=700,
|
||||||
|
height=400,
|
||||||
|
modal=True,
|
||||||
|
callback=dataflux.callbacks.menu.window_file_dialog_load_lap_ok,
|
||||||
|
user_data=state,
|
||||||
|
):
|
||||||
|
dpg.add_file_extension(".csv")
|
||||||
|
|||||||
@@ -12,13 +12,20 @@ import serial.tools.list_ports
|
|||||||
from dataflux.state import AppState
|
from dataflux.state import AppState
|
||||||
from dataflux.tags import (
|
from dataflux.tags import (
|
||||||
GRAPH_SERIES_SPEED,
|
GRAPH_SERIES_SPEED,
|
||||||
|
GRAPH_SERIES_SPEED_LR,
|
||||||
GRAPH_SERIES_TENG,
|
GRAPH_SERIES_TENG,
|
||||||
|
GRAPH_SERIES_TENG_LR,
|
||||||
GRAPH_SERIES_VBAT,
|
GRAPH_SERIES_VBAT,
|
||||||
|
GRAPH_SERIES_VBAT_LR,
|
||||||
|
GRAPH_X_AXIS_SPEED_LR,
|
||||||
|
GRAPH_X_AXIS_TENG_LR,
|
||||||
|
GRAPH_X_AXIS_VBAT_LR,
|
||||||
LIVE_DATA_TENG_VALUE,
|
LIVE_DATA_TENG_VALUE,
|
||||||
LIVE_DATA_UTC_TIME_VALUE,
|
LIVE_DATA_UTC_TIME_VALUE,
|
||||||
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_AUTOSAVE_BUFFERS,
|
||||||
)
|
)
|
||||||
from dataflux.ui.routines.serial import append_text_to_console
|
from dataflux.ui.routines.serial import append_text_to_console
|
||||||
|
|
||||||
@@ -37,10 +44,11 @@ def ui_worker(state: AppState):
|
|||||||
last_teng: str = ""
|
last_teng: str = ""
|
||||||
no_data_written = False
|
no_data_written = False
|
||||||
while state.running:
|
while state.running:
|
||||||
# if state.autosave_enabled:
|
if state.autosave_enabled:
|
||||||
# dpg.set_value(MENU_FILE_AUTOSAVE_BUFFERS, True)
|
dpg.set_value(MENU_FILE_AUTOSAVE_BUFFERS, True)
|
||||||
# else:
|
else:
|
||||||
# dpg.set_value(MENU_FILE_AUTOSAVE_BUFFERS, False)
|
dpg.set_value(MENU_FILE_AUTOSAVE_BUFFERS, False)
|
||||||
|
|
||||||
now = datetime.now(timezone.utc)
|
now = datetime.now(timezone.utc)
|
||||||
formatted = now.strftime("%H:%M:%S")
|
formatted = now.strftime("%H:%M:%S")
|
||||||
|
|
||||||
@@ -56,6 +64,33 @@ def ui_worker(state: AppState):
|
|||||||
else:
|
else:
|
||||||
append_text_to_console(text)
|
append_text_to_console(text)
|
||||||
|
|
||||||
|
if state.lap_recap_updated:
|
||||||
|
state.lap_recap_updated = False
|
||||||
|
timestamps = state.lap_recap_buffers.timestamp
|
||||||
|
|
||||||
|
if timestamps:
|
||||||
|
t0 = timestamps[0]
|
||||||
|
x_common = [(t - t0) / 100.0 for t in timestamps]
|
||||||
|
else:
|
||||||
|
x_common = []
|
||||||
|
dpg.set_value(
|
||||||
|
GRAPH_SERIES_SPEED_LR,
|
||||||
|
[x_common, state.lap_recap_buffers.speed],
|
||||||
|
)
|
||||||
|
dpg.set_value(
|
||||||
|
GRAPH_SERIES_VBAT_LR,
|
||||||
|
[x_common, state.lap_recap_buffers.vbat],
|
||||||
|
)
|
||||||
|
dpg.set_value(
|
||||||
|
GRAPH_SERIES_TENG_LR,
|
||||||
|
[x_common, state.lap_recap_buffers.teng],
|
||||||
|
)
|
||||||
|
axis_min = x_common[0]
|
||||||
|
axis_max = x_common[-1]
|
||||||
|
dpg.set_axis_limits(GRAPH_X_AXIS_SPEED_LR, ymin=axis_min, ymax=axis_max)
|
||||||
|
dpg.set_axis_limits(GRAPH_X_AXIS_VBAT_LR, ymin=axis_min, ymax=axis_max)
|
||||||
|
dpg.set_axis_limits(GRAPH_X_AXIS_TENG_LR, ymin=axis_min, ymax=axis_max)
|
||||||
|
|
||||||
if state.lora_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
|
||||||
|
|||||||
Reference in New Issue
Block a user