Updated docs and updated API to fetch signal descriptors in all modes

API and Protocol docs have been updated (GPT 5.6)

The API methods for fetching a sepcific signal descriptor and fetching
all signal descriptors now work in offline mode, the core_tui.py example
has been updated to fit the new tri mode architecture and loading
recording offload -> processing and then processing -> playback engine
all works
This commit is contained in:
h3cx committed 2026-09-21 13:33:55 +02:00
1 parent 47773df572
commit eab0718a72
6 files changed
+356 -596

No files matched your search

+80 -29
View File
@@ -102,6 +102,8 @@ class Dashboard:
self.config = config
self.log_handler = log_handler
self.running = True
self.mode_selection_open = False
self.offline_loaded = False
self.playback_loaded = False
self.message = "Waiting for a connector"
self.message_style = "dim"
@@ -110,12 +112,14 @@ class Dashboard:
try:
if key.lower() == "q":
self.running = False
elif self.mode_selection_open:
self.select_mode(key)
elif key.lower() == "r":
self.toggle_recording()
elif key.lower() == "m":
self.toggle_mode()
self.open_mode_selection()
elif key.lower() == "l":
self.load_playback()
self.load_current_stage()
elif key == " ":
self.toggle_playback()
except DynaLabError as error:
@@ -128,35 +132,64 @@ class Dashboard:
if self.core.get_recording_state():
self.core.stop_recording()
self.set_message("Recording stopped and prepared for playback", "green")
self.set_message("Recording stopped; select Offline and press L to load it", "green")
else:
self.core.start_recording()
self.set_message("Recording started", "bold red")
def toggle_mode(self) -> None:
def open_mode_selection(self) -> None:
if self.core.get_recording_state():
self.set_message("Stop recording before changing mode", "yellow")
return
if self.core.get_mode() == "realtime":
self.core.set_mode("playback")
self.set_message(
"Playback mode selected; press L to load a recording", "green"
)
else:
if self.core.get_playback_state():
self.core.pause()
self.core.set_mode("realtime")
self.set_message("Realtime mode selected", "green")
self.mode_selection_open = True
self.set_message("Choose a mode: 1 Realtime, 2 Offline, 3 Playback", "cyan")
def load_playback(self) -> None:
if self.core.get_mode() != "playback":
self.set_message("Switch to playback mode before loading", "yellow")
def select_mode(self, key: str) -> None:
modes = {"1": "realtime", "2": "offline", "3": "playback"}
if key == "\x1b" or key.lower() == "m":
self.mode_selection_open = False
self.set_message("Mode selection cancelled", "dim")
return
self.core.load_playback_engine()
self.playback_loaded = True
self.set_message("Internal processing buffer loaded", "green")
mode = modes.get(key)
if mode is None:
self.set_message("Choose 1 Realtime, 2 Offline, 3 Playback, or Esc", "yellow")
return
if self.core.get_playback_state() and mode != "playback":
self.core.pause()
self.core.set_mode(mode)
self.mode_selection_open = False
if mode == "offline":
self.set_message("Offline mode selected; press L to load the recording", "green")
elif mode == "playback":
self.set_message(
"Playback mode selected; press L to load the offline buffer", "green"
)
else:
self.set_message("Realtime mode selected", "green")
def load_current_stage(self) -> None:
mode = self.core.get_mode()
if mode == "offline":
self.core.load_processing_buffer()
self.offline_loaded = True
self.playback_loaded = False
self.set_message(
"Recording loaded into the offline buffer; select Playback and press L",
"green",
)
return
if mode == "playback":
self.core.load_playback_engine()
self.playback_loaded = True
self.set_message("Offline buffer loaded into playback", "green")
return
self.set_message("Select Offline or Playback before loading", "yellow")
def toggle_playback(self) -> None:
if self.core.get_mode() != "playback":
@@ -190,8 +223,8 @@ class Dashboard:
style="bold red" if recording else "dim",
),
Text(
self.playback_status(playing),
style="bold green" if playing else "dim",
self.buffer_status(mode, playing),
style="bold green" if playing or self.offline_loaded else "dim",
),
)
@@ -218,8 +251,8 @@ class Dashboard:
value_table.add_row("Waiting for connector…", "", "—", "")
controls = Text.from_markup(
"[bold]R[/bold] Record [bold]M[/bold] Mode "
"[bold]L[/bold] Load [bold]Space[/bold] Play/Pause "
"[bold]R[/bold] Record [bold]M[/bold] Choose mode "
"[bold]L[/bold] Load stage [bold]Space[/bold] Play/Pause "
"[bold]Q[/bold] Quit"
)
log_table = Table.grid(expand=True, padding=(0, 1))
@@ -239,7 +272,7 @@ class Dashboard:
for _ in range(VISIBLE_LOG_LINES - max(1, len(log_entries))):
log_table.add_row("", "", "")
return Group(
panels = [
Panel(
status,
title="DynaLab Core",
@@ -249,19 +282,37 @@ class Dashboard:
Panel(value_table, title="Live values", border_style="cyan"),
Panel(Text(self.message, style=self.message_style), title="Status"),
Panel(controls, title="Hotkeys", border_style="blue"),
]
if self.mode_selection_open:
mode_picker = Table.grid(expand=True, padding=(0, 2))
mode_picker.add_column(width=3, style="bold cyan")
mode_picker.add_column(ratio=1)
mode_picker.add_row("1", "Realtime — receive connector data and record")
mode_picker.add_row("2", "Offline — load a completed recording into a processing buffer")
mode_picker.add_row("3", "Playback — load the offline buffer and replay it")
mode_picker.add_row("Esc", "Cancel")
panels.append(
Panel(mode_picker, title="Choose mode", border_style="yellow")
)
panels.append(
Panel(
log_table,
title=f"Logs · latest {VISIBLE_LOG_LINES}",
border_style="magenta",
),
)
)
return Group(*panels)
def playback_status(self, playing: bool) -> str:
def buffer_status(self, mode: str, playing: bool) -> str:
if playing:
return "Playback: PLAYING"
if self.playback_loaded:
if mode == "playback" and self.playback_loaded:
return "Playback: LOADED"
return "Playback: NOT LOADED"
if mode == "offline" and self.offline_loaded:
return "Offline: LOADED"
if self.offline_loaded:
return "Offline buffer: READY"
return "Offline buffer: NOT LOADED"
def main() -> None: