diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..50b3531 --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +# OS / editor +.DS_Store +Thumbs.db +.idea/ +.vscode/ + +# Python / Django +__pycache__/ +*.py[cod] +*.pyo +*.pyd +.Python +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +.coverage +.coverage.* +htmlcov/ +.tox/ +.nox/ + +# Python virtual environments +.venv/ +venv/ +env/ +ENV/ + +# Django local data +backend/*.sqlite3 +backend/*.sqlite3-* +backend/db.sqlite3 +backend/media/ +backend/staticfiles/ + +# Environment files +.env +.env.* +!.env.example +backend/.env +backend/.env.* +frontend/.env +frontend/.env.* +!frontend/.env.example + +# Logs +*.log +logs/ + +# Frontend / Node / Vite +frontend/node_modules/ +frontend/dist/ +frontend/.vite/ +frontend/.cache/ +frontend/coverage/ +frontend/npm-debug.log* +frontend/yarn-debug.log* +frontend/yarn-error.log* +frontend/pnpm-debug.log* + +# Local build / temporary files +tmp/ +temp/ +*.tmp + +# Optional local config overrides +config/*.local.yaml +config/*.local.yml diff --git a/SampleCData/omnisocket_video_receiver.py b/SampleCData/omnisocket_video_receiver.py deleted file mode 100644 index 6b5d53a..0000000 --- a/SampleCData/omnisocket_video_receiver.py +++ /dev/null @@ -1,64 +0,0 @@ -"""Minimal video-plane sample that receives frames with recv_into().""" - -from __future__ import annotations - -from pathlib import Path -import sys - -import yaml - -try: - from omnisocket import MSG_TYPE_BINARY, Session, VIDEO_DEFAULTS -except ImportError: - sys.path.insert(0, str(Path(__file__).resolve().parent / "python")) - from omnisocket import MSG_TYPE_BINARY, Session, VIDEO_DEFAULTS - - -def load_config() -> dict: - config_path = Path(__file__).resolve().parent / "config" / "omnisocket_demo.yaml" - if not config_path.exists(): - return {} - with config_path.open("r", encoding="utf-8") as file: - return yaml.safe_load(file) or {} - - -def main() -> None: - config = load_config() - transport_cfg = config.get("transport", {}) - video_cfg = config.get("video_receiver", {}) - - session = Session() - session.connect( - server_addr=str(transport_cfg.get("server_addr", "127.0.0.1:10909")), - peer_id=str(video_cfg.get("peer_id", "peer-a-video")), - relay_via=str(transport_cfg.get("relay_via", "")), - bind_ip=str(transport_cfg.get("bind_ip", "")), - bind_device=str(transport_cfg.get("bind_device", "")), - **VIDEO_DEFAULTS, - ) - - buffer = bytearray(int(video_cfg.get("buffer_bytes", 65536))) - frame_count = 0 - try: - while True: - meta = session.recv_into(buffer, timeout_ms=1000) - if meta is None: - continue - if meta["msg_type"] != MSG_TYPE_BINARY: - print(f"ignore non-binary message: {meta}") - continue - frame = bytes(buffer[: meta["body_len"]]) - sequence = int.from_bytes(frame[:8], "big") if len(frame) >= 8 else -1 - print( - f"received frame={frame_count} remote_seq={sequence} " - f"bytes={meta['body_len']} from={meta['from']}" - ) - frame_count += 1 - except KeyboardInterrupt: - pass - finally: - session.close() - - -if __name__ == "__main__": - main() diff --git a/SampleCData/omnisocket_video_sender.py b/SampleCData/omnisocket_video_sender.py deleted file mode 100644 index 840763c..0000000 --- a/SampleCData/omnisocket_video_sender.py +++ /dev/null @@ -1,66 +0,0 @@ -"""Minimal video-plane sample that sends fixed-size binary frames over KCP.""" - -from __future__ import annotations - -from pathlib import Path -import os -import sys -import time - -import yaml - -try: - from omnisocket import Session, VIDEO_DEFAULTS -except ImportError: - sys.path.insert(0, str(Path(__file__).resolve().parent / "python")) - from omnisocket import Session, VIDEO_DEFAULTS - - -def load_config() -> dict: - config_path = Path(__file__).resolve().parent / "config" / "omnisocket_demo.yaml" - if not config_path.exists(): - return {} - with config_path.open("r", encoding="utf-8") as file: - return yaml.safe_load(file) or {} - - -def main() -> None: - config = load_config() - transport_cfg = config.get("transport", {}) - video_cfg = config.get("video_sender", {}) - - server_addr = str(transport_cfg.get("server_addr", "127.0.0.1:10909")) - relay_via = str(transport_cfg.get("relay_via", "")) - bind_ip = str(transport_cfg.get("bind_ip", "")) - bind_device = str(transport_cfg.get("bind_device", "")) - peer_id = str(video_cfg.get("peer_id", "peer-b-video")) - target_peer = str(video_cfg.get("target_peer", "peer-a-video")) - frame_bytes = int(video_cfg.get("frame_bytes", 30720)) - frame_interval_ms = int(video_cfg.get("frame_interval_ms", 66)) - - session = Session() - session.connect( - server_addr=server_addr, - peer_id=peer_id, - relay_via=relay_via, - bind_ip=bind_ip, - bind_device=bind_device, - **VIDEO_DEFAULTS, - ) - - sequence = 0 - try: - while True: - frame = sequence.to_bytes(8, "big") + os.urandom(max(0, frame_bytes - 8)) - session.send(to=target_peer, data=frame) - print(f"sent frame={sequence} bytes={len(frame)}") - sequence += 1 - time.sleep(frame_interval_ms / 1000.0) - except KeyboardInterrupt: - pass - finally: - session.close() - - -if __name__ == "__main__": - main() diff --git a/backend/monitoring/services.py b/backend/monitoring/services.py index 29927a4..feb00c2 100644 --- a/backend/monitoring/services.py +++ b/backend/monitoring/services.py @@ -18,7 +18,9 @@ JPEG_FRAME_DIR = WORKSPACE_ROOT / "RobotDataShow" / "jpeg-frames" GEOSTREAM_JSON_PATH = WORKSPACE_ROOT / "GeoStream" / "gps_latest.json" GEOSTREAM_STALE_SECONDS = 15 SAMPLE_CDATA_DIR = PROJECT_ROOT / "SampleCData" -OMNISOCKET_CONFIG_PATH = SAMPLE_CDATA_DIR / "config" / "omnisocket_demo.yaml" +CONFIG_DIR = PROJECT_ROOT / "config" +PRIMARY_OMNISOCKET_CONFIG_PATH = CONFIG_DIR / "omnisocket_demo.yaml" +LEGACY_OMNISOCKET_CONFIG_PATH = SAMPLE_CDATA_DIR / "config" / "omnisocket_demo.yaml" VIDEO_SOURCE_MODE = os.getenv("VIDEO_SOURCE_MODE", "auto").strip().lower() OMNISOCKET_FRAME_FRESH_SECONDS = 2.0 @@ -27,6 +29,12 @@ def utc_iso_now() -> str: return datetime.now(UTC).isoformat(timespec="seconds").replace("+00:00", "Z") +def resolve_omnisocket_config_path() -> Path: + if PRIMARY_OMNISOCKET_CONFIG_PATH.exists(): + return PRIMARY_OMNISOCKET_CONFIG_PATH + return LEGACY_OMNISOCKET_CONFIG_PATH + + class OmniSocketVideoReceiver: def __init__(self) -> None: self._lock = threading.Lock() @@ -88,16 +96,17 @@ class OmniSocketVideoReceiver: # transport + video_receiver。 # 即使配置文件不存在,也允许回退到默认值继续运行。 config: dict[str, Any] = {} - if OMNISOCKET_CONFIG_PATH.exists(): + config_path = resolve_omnisocket_config_path() + if config_path.exists(): try: try: import yaml # type: ignore - with OMNISOCKET_CONFIG_PATH.open("r", encoding="utf-8") as file: + with config_path.open("r", encoding="utf-8") as file: config = yaml.safe_load(file) or {} except ImportError: # 如果当前环境没有 PyYAML,就用一个足够支撑当前 demo 配置的简化解析器。 - config = self._load_simple_yaml_config(OMNISOCKET_CONFIG_PATH) + config = self._load_simple_yaml_config(config_path) except Exception as error: # pragma: no cover - 可选依赖 self._last_error = f"config load failed: {error}" @@ -268,6 +277,7 @@ class OmniSocketVideoReceiver: def get_status(self) -> dict[str, Any]: self.ensure_started() config = self._load_config() + config_path = resolve_omnisocket_config_path() transport_cfg = config.get("transport", {}) video_cfg = config.get("video_receiver", {}) with self._lock: @@ -282,7 +292,7 @@ class OmniSocketVideoReceiver: "frames_received": self._frames_received, "latest_sequence": self._latest_sequence, "last_error": self._last_error, - "config_path": str(OMNISOCKET_CONFIG_PATH), + "config_path": str(config_path), "server_addr": str(transport_cfg.get("server_addr", "")), "relay_via": str(transport_cfg.get("relay_via", "")), "peer_id": str(video_cfg.get("peer_id", "")), diff --git a/SampleCData/config/omnisocket_demo.yaml b/config/omnisocket_demo.yaml similarity index 100% rename from SampleCData/config/omnisocket_demo.yaml rename to config/omnisocket_demo.yaml