支持传感器独立校准文件与批量配置启动

保留逐台添加,并通过显式配置一次连接全组;失败时撤销本批新增连接。通过 35 项回归测试、真实 SDK 校准与 1–4 路验证、Ruff 和文档检查。
This commit is contained in:
ykcheungab
2026-10-14 16:11:15 +08:00
parent 133a98e418
commit a723e96dcc
7 changed files with 460 additions and 24 deletions
+38 -10
View File
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
"""Headless UART finger service for 1-4 sensors; see ../docs/uart.md.
The login console and data UART must be separate ports. Cameras are added
manually via UART before starting output. All force records include a 1-based sensor ID.
The login console and data UART must be separate ports. Cameras are selected
explicitly by UART commands or a startup setup file. All force records include a sensor ID.
Importing this module opens no devices; acquisition is owned by finger_api.
"""
@@ -137,20 +137,40 @@ class UartFingerController:
return True
command = parts[0].lower()
if command == "add":
if len(parts) not in (2, 3):
self.reply("ERR command expected add SOURCE [CONFIG]")
elif self._streaming:
if self._streaming:
self.reply("ERR add stop streaming first")
else:
parser = argparse.ArgumentParser(prog="add", add_help=False, exit_on_error=False)
parser.add_argument("source")
parser.add_argument("config", nargs="?")
parser.add_argument("--calibration")
try:
args = parser.parse_args(parts[1:])
except (argparse.ArgumentError, SystemExit):
self.reply("ERR command expected add SOURCE [CONFIG] [--calibration PATH]")
return True
try:
sensor_id = self.bank.add(
parse_source(parts[1]), config=parts[2] if len(parts) == 3 else None,
parse_source(args.source), config=args.config, cal_path=args.calibration,
)
except Exception as exc:
logger.exception("Sensor addition failed")
self.reply(f"ERR add {exc}")
else:
self.reply(f"OK added {sensor_id}")
elif command == "load":
if len(parts) != 2:
self.reply("ERR command expected load SETUP.json")
elif self._streaming:
self.reply("ERR load stop streaming first")
else:
try:
sensor_ids = self.bank.add_setup(parts[1])
except Exception as exc:
logger.exception("Setup loading failed")
self.reply(f"ERR load {exc}")
else:
self.reply("OK loaded " + " ".join(map(str, sensor_ids)))
elif command not in {"start", "stop", "read", "quit"}:
self.reply(f"ERR unknown {command}")
elif len(parts) != 1:
@@ -172,9 +192,11 @@ class UartFingerController:
self.reply("OK quit")
return not self._should_quit
def serve(self):
def serve(self, *, start=False):
if start and not self.bank.runtimes:
raise RuntimeError("no sensors added")
self.reply(f"OK ready {len(self.bank.sources)}")
self._streaming = False
self._streaming = start
while not self._should_quit:
for _ in range(16):
line = self._poll_command_line(blocking=not self._streaming)
@@ -204,10 +226,14 @@ def main(argv=None):
parser.add_argument("--timeout", type=float, default=0.1, help="idle read timeout in seconds")
parser.add_argument("--write-timeout", type=float, default=1.0)
parser.add_argument("--config", "-c", default="finger", help="default config for add commands")
parser.add_argument("--setup", help="JSON file listing sources, configs and calibrations to connect")
parser.add_argument("--start", action="store_true", help="start output after loading --setup")
parser.add_argument("--no-loop-video", dest="loop_video", action="store_false")
parser.add_argument("--show", action="store_true", help="local desktop debug only")
parser.add_argument("--verbose", action="store_true")
args = parser.parse_args(argv)
if args.start and not args.setup:
parser.error("--start requires --setup")
if args.baud <= 0:
parser.error("baud must be positive")
if any(not math.isfinite(t) or t <= 0 for t in (args.timeout, args.write_timeout)):
@@ -219,10 +245,12 @@ def main(argv=None):
ser = None
result = 0
try:
if args.setup:
controller.bank.add_setup(args.setup)
ser = open_serial(args.port, args.baud, args.timeout, args.write_timeout)
controller.ser = ser
print(f"UART service on {args.port} @ {args.baud}; waiting for add SOURCE, then start", file=sys.stderr, flush=True)
controller.serve()
print(f"UART service on {args.port} @ {args.baud}; {len(controller.bank.runtimes)} sensors ready", file=sys.stderr, flush=True)
controller.serve(start=args.start)
except KeyboardInterrupt:
print("Interrupted.", file=sys.stderr)
except Exception: