microfix
This commit is contained in:
@@ -31,7 +31,9 @@ from contextlib import suppress
|
|||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
import shutil
|
||||||
import statistics
|
import statistics
|
||||||
|
import subprocess
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
@@ -65,6 +67,60 @@ DO8_FREQ_REF_ARGS = [
|
|||||||
]
|
]
|
||||||
COLLECTOR_PATH = "build/bin/kamil_adc_collector"
|
COLLECTOR_PATH = "build/bin/kamil_adc_collector"
|
||||||
|
|
||||||
|
# Hardware the daemon / a prior collector may be holding, and how to free it.
|
||||||
|
# Mirrors what start.sh does before an interactive launch (stop the daemon, kill
|
||||||
|
# an orphaned collector) so this tool can grab the L-Card E-502 + lasers too.
|
||||||
|
RADAR_SERVICE_NAME = "radar.service"
|
||||||
|
COLLECTOR_PROCESS_PATTERN = "kamil_adc_collector"
|
||||||
|
# The E-502 is not reacquirable the instant it is released; wait before opening.
|
||||||
|
_DEVICE_SETTLE_SECONDS = 8.0
|
||||||
|
|
||||||
|
|
||||||
|
def _release_radar_hardware(*, settle_seconds: float = _DEVICE_SETTLE_SECONDS) -> None:
|
||||||
|
"""Free the L-Card E-502 + lasers before we open our own collector.
|
||||||
|
|
||||||
|
Mirrors ``start.sh`` on an interactive launch: stop the headless
|
||||||
|
``radar.service`` daemon (if active) so it releases the hardware, then kill any
|
||||||
|
orphaned ``kamil_adc_collector`` a prior (e.g. SSH-killed) run left holding the
|
||||||
|
device. Both steps are best-effort and non-fatal — the collector's own
|
||||||
|
open-retry covers the residual settle time — this just removes the usual reason
|
||||||
|
it never frees up. Passwordless ``sudo systemctl stop radar.service`` is
|
||||||
|
provisioned in ``/etc/sudoers.d/radar``.
|
||||||
|
"""
|
||||||
|
freed = False
|
||||||
|
|
||||||
|
systemctl = shutil.which("systemctl")
|
||||||
|
if systemctl is not None:
|
||||||
|
is_active = subprocess.run(
|
||||||
|
[systemctl, "is-active", "--quiet", RADAR_SERVICE_NAME],
|
||||||
|
check=False,
|
||||||
|
).returncode == 0
|
||||||
|
if is_active:
|
||||||
|
logger.info("Stopping %s so it releases the radar hardware...", RADAR_SERVICE_NAME)
|
||||||
|
stopped = subprocess.run(
|
||||||
|
["sudo", systemctl, "stop", RADAR_SERVICE_NAME], check=False
|
||||||
|
).returncode == 0
|
||||||
|
if stopped:
|
||||||
|
freed = True
|
||||||
|
else:
|
||||||
|
logger.warning(
|
||||||
|
"Could not stop %s (need passwordless sudo?); continuing anyway",
|
||||||
|
RADAR_SERVICE_NAME,
|
||||||
|
)
|
||||||
|
|
||||||
|
pkill = shutil.which("pkill")
|
||||||
|
if pkill is not None:
|
||||||
|
# pkill returns 0 when it matched & signalled at least one process.
|
||||||
|
if subprocess.run(
|
||||||
|
[pkill, "-9", "-f", COLLECTOR_PROCESS_PATTERN], check=False
|
||||||
|
).returncode == 0:
|
||||||
|
logger.info("Killed orphaned %s process(es) holding the device", COLLECTOR_PROCESS_PATTERN)
|
||||||
|
freed = True
|
||||||
|
|
||||||
|
if freed:
|
||||||
|
logger.info("Waiting %.0fs for the L-Card E-502 to settle...", settle_seconds)
|
||||||
|
time.sleep(settle_seconds)
|
||||||
|
|
||||||
|
|
||||||
def _open_with_retry(service: KamilAdcService, *, attempts: int = 4, delay_s: float = 8.0) -> None:
|
def _open_with_retry(service: KamilAdcService, *, attempts: int = 4, delay_s: float = 8.0) -> None:
|
||||||
"""Open the collector, retrying the transient E-502 device-busy after a close.
|
"""Open the collector, retrying the transient E-502 device-busy after a close.
|
||||||
@@ -519,6 +575,11 @@ def main() -> int:
|
|||||||
parser.add_argument("--warmup", type=int, default=10, help="Sweeps to discard first (default 10)")
|
parser.add_argument("--warmup", type=int, default=10, help="Sweeps to discard first (default 10)")
|
||||||
parser.add_argument("--apply", action="store_true", help="Write the calibration back to --config")
|
parser.add_argument("--apply", action="store_true", help="Write the calibration back to --config")
|
||||||
parser.add_argument("--no-laser", action="store_true", help="Skip laser setup (already running)")
|
parser.add_argument("--no-laser", action="store_true", help="Skip laser setup (already running)")
|
||||||
|
parser.add_argument(
|
||||||
|
"--no-release",
|
||||||
|
action="store_true",
|
||||||
|
help="Do not stop radar.service / kill orphaned collectors before opening",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--diagnose",
|
"--diagnose",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -541,6 +602,11 @@ def main() -> int:
|
|||||||
config.radar.kamil_adc.executable_path = COLLECTOR_PATH
|
config.radar.kamil_adc.executable_path = COLLECTOR_PATH
|
||||||
config.radar.kamil_adc.args = list(DO8_FREQ_REF_ARGS)
|
config.radar.kamil_adc.args = list(DO8_FREQ_REF_ARGS)
|
||||||
|
|
||||||
|
# Free the device the headless daemon / an orphaned collector may be holding,
|
||||||
|
# so opening our own collector does not fail with E-502 device-busy.
|
||||||
|
if not args.no_release:
|
||||||
|
_release_radar_hardware()
|
||||||
|
|
||||||
if not args.no_laser:
|
if not args.no_laser:
|
||||||
logger.info("Applying laser control...")
|
logger.info("Applying laser control...")
|
||||||
apply_kamil_adc_laser_control(config)
|
apply_kamil_adc_laser_control(config)
|
||||||
|
|||||||
Reference in New Issue
Block a user