Manual_Photographer/take_photo.py
2026-04-12 18:53:20 +04:00

117 lines
3.4 KiB
Python

#!/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())