Files
radar_system/python_app/gui/runtime/history.py
T
2026-10-06 12:32:26 +03:00

103 lines
4.2 KiB
Python

"""Helpers for GUI-side runtime history management."""
from __future__ import annotations
from collections import deque
from dataclasses import astuple
from python_app.models.collection_identity import collection_key
from python_app.models.dataset_model import ResultCollection
from python_app.models.run_config_schema import RunConfigModel
from python_app.orchestration.preprocess_assets import PREPROCESS_ASSET_NAMES, preprocess_asset_model
def record_result_history(
result_history: deque[ResultCollection],
collection: ResultCollection,
) -> bool:
"""Record a result, keeping the history in acquisition order; return whether it is new.
A result of a sweep already present replaces it (the sweep was reprocessed).
Any other result goes after the newest older one: normally at the end, as the
result of a new sweep, but the processor reprocessing its retained sweeps also
delivers sweeps whose first result the GUI missed, and appending those would
show them out of order.
"""
if result_history.maxlen == 0:
return False
key = collection_key(collection)
position = len(result_history)
while position > 0:
existing = result_history[position - 1]
if collection_key(existing) == key:
result_history[position - 1] = collection
return False
if existing.capture_start_ns < collection.capture_start_ns:
break
position -= 1
if position == len(result_history):
result_history.append(collection)
return True
if len(result_history) == result_history.maxlen:
if position == 0:
return False # older than everything kept, so it would be the first to go
result_history.popleft()
position -= 1
result_history.insert(position, collection)
return True
def build_processor_run_signature(
config: RunConfigModel,
) -> tuple[object, ...]:
"""Settings consumed at processor startup, rather than through the live file.
Excludes the preprocess set names on purpose: the data_processor does not consume
calibration/reference sets (those are applied upstream by the data_preprocessor), so
changing a set must NOT restart the processor. Restarting it would also tear down the
locator TCP server it hosts and drop every connected client. Shape, ring endpoints,
locator settings, live-file path and polling interval require a restart.
"""
combos_signature = tuple((int(combo.input), int(combo.output)) for combo in config.combos)
sweep_points_signature: object = "adc" if config.is_kamil_adc else int(config.radar.sweep.points)
return (
str(config.radar.model),
str(config.radar.driver_mode),
str(config.radar.serial),
tuple(str(value) for value in config.radar.multi_device.slave_serials),
bool(config.radar.multi_device.force_external_reference),
float(config.radar.sweep.start_hz),
float(config.radar.sweep.stop_hz),
sweep_points_signature,
float(config.radar.sweep.if_bandwidth_hz),
float(config.radar.sweep.power_dbm),
str(config.input_switch.driver_mode),
int(config.input_switch.positions),
str(config.output_switch.driver_mode),
int(config.output_switch.positions),
combos_signature,
astuple(config.rings),
astuple(config.runtime.locator_server),
str(config.runtime.processing_live_config_path),
int(config.runtime.idle_sleep_ms),
)
def build_run_history_signature(
config: RunConfigModel,
) -> tuple[object, ...]:
"""Reset display history when startup settings or preprocessing change.
Display history still resets when the reference/calibration set changes (the on-screen
B-scan would otherwise mix old- and new-reference frames), even though the processor
process itself is intentionally kept alive across that change.
"""
preprocess_signature = tuple(astuple(preprocess_asset_model(config, key)) for key in PREPROCESS_ASSET_NAMES)
notch = config.preprocess.notch
notch_signature = (
bool(notch.enabled), tuple(tuple(band) for band in notch.bands_hz),
float(notch.taper_width_hz), str(notch.taper_type),
)
return build_processor_run_signature(config) + (preprocess_signature, notch_signature)