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

109 lines
4.4 KiB
Python

"""Qt-free defaults derived from a radar profile and GUI history capacities."""
import os
from pathlib import Path
from python_app.models.run_config_schema import RunConfigModel, ComboModel
from python_app.models.gui_profile_schema import (
GuiStateModel, GuiSwitchStateModel, GuiProcessingStateModel, GuiGprStateModel,
GuiLegacyGprStateModel, GuiSar3dStateModel, GuiDataActionsStateModel,
)
GUI_SAVE_HISTORY_LIMIT = 1000
# Mirrors data_processor replay_history_limit, bounded further by SHM capacities.
CPP_BSCAN_REPLAY_WINDOW = 50
def format_combos_text_from_config(config: RunConfigModel) -> str:
"""Render configured combos for UI text editor, keeping full matrix as empty."""
if config.is_multi_device:
return ",".join(
f"{int(combo.input)}:{int(combo.output)}"
for combo in config.build_runtime_combos()
)
combos = list(config.combos)
full_combos = config.build_full_combos(config.input_switch.positions, config.output_switch.positions)
if len(combos) == len(full_combos) and all(
int(left.input) == int(right.input) and int(left.output) == int(right.output)
for left, right in zip(combos, full_combos, strict=True)
):
return ""
return ",".join(f"{int(combo.input)}:{int(combo.output)}" for combo in combos)
def env_enabled(name: str) -> bool:
return os.environ.get(name, "").strip().lower() in {"1", "true", "yes", "on"}
def _positions(geometry: set[int], combos: set[int]) -> str:
return ",".join(map(str, sorted(geometry & combos or geometry)))
def default_gpr_visible_x_bounds_from_config(config: RunConfigModel) -> tuple[float, float]:
"""Build default visible X-range for GPR object-only rendering."""
x_values = [float(entry.x_m) for entry in config.gpr.tx_geometry]
x_values.extend(float(entry.x_m) for entry in config.gpr.rx_geometry)
if not x_values:
return (-2.0, 2.0)
margin_m = 2.0
return (min(x_values) - margin_m, max(x_values) + margin_m)
def cpp_bscan_replay_window_for_config(config: RunConfigModel) -> int:
"""Return how many newest collections the C++ processor re-processes on a
live B-scan settings change.
Deliberately reproduces `replay_history_limit()` in `data_processor.cpp`
formula-for-formula. The ring capacities matter because `ShmRing` overwrites
the oldest unread slot on overflow, so a replay burst must fit in the results
ring for the GUI to receive all of it.
This is the single place to change if the replay window ever becomes
configurable on the C++ side.
"""
return max(
1,
min(
int(config.rings.preprocessed.capacity),
int(config.rings.results.capacity),
CPP_BSCAN_REPLAY_WINDOW,
),
)
def default_gui_state_for_config(config: RunConfigModel, project_root: Path) -> GuiStateModel:
"""Build fallback GUI-only defaults for a stable run config."""
default_combo = config.combos[0] if config.combos else ComboModel(input=0, output=0)
default_mode = "single" if len(config.combos) == 1 else "text"
inputs = _positions({entry.input_pos for entry in config.gpr.rx_geometry}, {combo.input for combo in config.combos})
outputs = _positions({entry.output_pos for entry in config.gpr.tx_geometry}, {combo.output for combo in config.combos})
default_gpr_x_min_m, default_gpr_x_max_m = default_gpr_visible_x_bounds_from_config(config)
return GuiStateModel(
switches=GuiSwitchStateModel(
combo_mode=default_mode,
combos_text=format_combos_text_from_config(config),
single_input=str(int(default_combo.input)),
single_output=str(int(default_combo.output)),
),
processing=GuiProcessingStateModel(
gpr=GuiGprStateModel(
input_positions=inputs,
output_positions=outputs,
visible_x_min_m=default_gpr_x_min_m,
visible_x_max_m=default_gpr_x_max_m,
),
legacy_gpr=GuiLegacyGprStateModel(
input_positions=inputs,
output_positions=outputs,
visible_x_min_m=default_gpr_x_min_m,
visible_x_max_m=default_gpr_x_max_m,
),
sar3d=GuiSar3dStateModel(
input_positions=inputs,
output_positions=outputs,
),
),
data_actions=GuiDataActionsStateModel(
save_path=str(project_root / "python_app/data/snapshots"),
),
)