#!/usr/bin/env python3 """ take_photo.py (Option A) - Capture ONE frame from teleimager ZMQ JPEG stream and save it. Why: - teleimager.image_server owns the RealSense device, so /dev/video0 is BUSY. - Instead of opening the camera, we subscribe to teleimager's ZMQ JPEG stream and save 1 frame. Env: TELEIMAGER_HOST default: 127.0.0.1 ZMQ_PORT default: 55555 PHOTOS_DIR default: ./photos PHOTO_PREFIX default: photo PHOTO_EXT default: jpg FRAME_TIMEOUT_S default: 3.0 Output: - prints saved absolute path as LAST LINE (so sanad_voice.py can log it) """ import os import sys import time from pathlib import Path from Logger import Logs sanad_logger = Logs() sanad_logger.LogEngine("G1_Logs", "take_photo") def eprint(*a): print(*a, file=sys.stderr) def next_name(out_dir: Path, prefix: str, ext: str) -> Path: """photo.jpg, photo(1).jpg, photo(2).jpg ...""" base = out_dir / f"{prefix}.{ext}" if not base.exists(): return base i = 1 while True: p = out_dir / f"{prefix}({i}).{ext}" if not p.exists(): return p i += 1 def recv_one_jpeg_zmq(host: str, port: int, timeout_s: float) -> bytes: import zmq # requires pyzmq in teleimager env ctx = zmq.Context.instance() sub = ctx.socket(zmq.SUB) sub.setsockopt(zmq.RCVHWM, 1) # keep only latest sub.setsockopt(zmq.LINGER, 0) sub.setsockopt_string(zmq.SUBSCRIBE, "") sub.connect(f"tcp://{host}:{port}") poller = zmq.Poller() poller.register(sub, zmq.POLLIN) deadline = time.monotonic() + timeout_s last = b"" try: # drain quickly to newest while time.monotonic() < deadline: remaining_ms = max(1, int((deadline - time.monotonic()) * 1000)) events = dict(poller.poll(timeout=min(200, remaining_ms))) if sub in events: msg = sub.recv() if msg: last = msg # keep newest # if we got something, we can return immediately return last raise TimeoutError("No JPEG frame received from ZMQ in time.") finally: try: sub.close() except Exception: pass def main(): host = os.getenv("TELEIMAGER_HOST", "127.0.0.1") port = int(os.getenv("ZMQ_PORT", os.getenv("TELEIMAGER_ZMQ_PORT", "55555"))) out_dir = Path(os.getenv("PHOTOS_DIR", "./photos")).expanduser().resolve() prefix = os.getenv("PHOTO_PREFIX", "photo").strip() or "photo" ext = os.getenv("PHOTO_EXT", "jpg").strip().lstrip(".") or "jpg" timeout_s = float(os.getenv("FRAME_TIMEOUT_S", "3.0")) out_dir.mkdir(parents=True, exist_ok=True) try: jpg = recv_one_jpeg_zmq(host, port, timeout_s) except Exception as e: eprint("[ERR] take_photo.py failed") eprint(f"[zmq] {e}") return 4 # (اختياري) تحقق أنه JPEG فعلاً if len(jpg) < 50: eprint("[ERR] take_photo.py failed") eprint("[zmq] Received too-small frame.") return 5 out_path = next_name(out_dir, prefix, ext) try: out_path.write_bytes(jpg) except Exception as e: eprint("[ERR] take_photo.py failed") eprint(f"[fs] Could not write file: {e}") return 6 # IMPORTANT: last line should be path only sanad_logger.print_and_log(str(out_path)) return 0 if __name__ == "__main__": raise SystemExit(main())