117 lines
3.4 KiB
Python
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())
|