some fixes

This commit is contained in:
Ayzen
2026-06-04 18:33:38 +03:00
parent eacea436a4
commit 22942d9dc9
26 changed files with 1352 additions and 153 deletions
+72 -1
View File
@@ -10,6 +10,11 @@ REQUIREMENTS_FILE="${PROJECT_ROOT}/requirements.txt"
PYTHON_CMD=""
PROFILE_PATH=""
# Single-instance coordination: the headless daemon and the interactive GUI
# must never run at once (they share the radar, SHM rings and locator port).
SERVICE_NAME="radar.service"
LOCK_FILE="/tmp/radar_system.lock"
SKIP_BUILD=0
BUILD_ONLY=0
CLEAN_SHM=0
@@ -154,6 +159,15 @@ ensure_python_dependencies() {
return
fi
if ((HEADLESS == 1)); then
# Headless = unattended (often offline) appliance: never attempt a network
# pip install that could hang or crash-loop the service. Provisioning is a
# one-time interactive step. Fail fast with a clear, actionable message.
echo "Python dependencies are missing and headless mode does not provision them." >&2
echo "Run an interactive './start.sh' once (online) to create the venv, then retry." >&2
exit 1
fi
echo "[start.sh] Installing Python dependencies into virtual environment..."
"${VENV_PIP}" install --upgrade pip
"${VENV_PIP}" install -r "${REQUIREMENTS_FILE}"
@@ -311,11 +325,58 @@ run_producer_only() {
exec "${PYTHON_CMD}" -m python_app.scripts.kamil_adc_raw_producer --config "${PROFILE_PATH}"
}
stop_headless_service() {
# Stop the headless daemon (if running) so it releases the radar, SHM rings
# and locator port before the interactive GUI/producer takes over. Relies on
# the passwordless sudoers rule installed by deploy/install-daemon.sh. Safe
# no-op when systemd or the unit is absent (is-active is false -> skip).
command -v systemctl >/dev/null 2>&1 || return 0
if systemctl is-active --quiet "${SERVICE_NAME}"; then
echo "[start.sh] Stopping ${SERVICE_NAME} so the GUI can take over the hardware..."
# Non-fatal: a sudo/systemctl failure must not abort the GUI launch under
# `set -e`. The single-instance lock below still prevents a real conflict.
if ! sudo systemctl stop "${SERVICE_NAME}"; then
echo "[start.sh] WARNING: failed to stop ${SERVICE_NAME}; relying on the instance lock." >&2
fi
fi
}
verify_cpp_binaries() {
# Guard the daemon's --skip-build path: refuse to run against a tree whose C++
# binaries were never built, instead of failing obscurely at spawn time.
local missing=0 bin
for bin in data_processor data_preprocessor; do
if [[ ! -x "${PROJECT_ROOT}/build/bin/${bin}" ]]; then
echo "Required binary missing or not executable: build/bin/${bin}" >&2
missing=1
fi
done
if ((missing == 1)); then
echo "Build the C++ binaries first: run ./start.sh without --skip-build, or 'make all'." >&2
exit 1
fi
}
acquire_single_instance_lock() {
# Fail fast if another instance already owns the hardware, instead of
# surfacing a cryptic 'shm ring busy' / 'port in use' later. fd 9 survives
# the exec into Python, so the lock is held for the whole app lifetime.
exec 9>"${LOCK_FILE}"
if ! flock -n 9; then
echo "[start.sh] Another radar instance already holds ${LOCK_FILE}; aborting." >&2
echo "[start.sh] Stop it first: sudo systemctl stop ${SERVICE_NAME} (or close the other run)." >&2
exit 1
fi
}
main() {
parse_args "$@"
resolve_profile_path
check_environment
if ((KAMIL_ADC_MODE == 0)); then
# Skip first-time provisioning (build headers, USB udev rule) in headless
# mode: the daemon runs unattended at boot as a non-root user and must not
# block on sudo. A fresh machine is provisioned by one interactive launch.
if ((KAMIL_ADC_MODE == 0 && HEADLESS == 0)); then
ensure_system_dependencies
ensure_usb_access_rules
fi
@@ -329,11 +390,21 @@ main() {
build_cpp_binaries
fi
verify_cpp_binaries
if ((BUILD_ONLY == 1)); then
echo "[start.sh] Build completed."
exit 0
fi
# Interactive GUI/producer launch: stop the headless daemon first so it
# frees the radar, SHM and locator port. The daemon itself runs --headless
# and skips this, so it never stops itself.
if ((HEADLESS == 0)); then
stop_headless_service
fi
acquire_single_instance_lock
if ((PRODUCER_ONLY == 1)); then
run_producer_only
fi