f34817d5b6
FastAPI backend + vanilla JS frontend for LiDAR/camera startup, camera settings, and rosbag recording, unifying the previous scan_gui/scan_gui_dual/ scan_gui_triple desktop tools into one web app.
56 lines
1.7 KiB
Python
56 lines
1.7 KiB
Python
"""Read back what ros2 bag already knows about a recording — duration,
|
|
message counts, per-topic counts — instead of just listing directory names.
|
|
Also a plain on-disk size sum (metadata.yaml doesn't carry a byte size in
|
|
the ros2 bag version installed here)."""
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
from pathlib import Path
|
|
from typing import Optional
|
|
|
|
import yaml
|
|
|
|
|
|
def dir_size_bytes(path: Path) -> int:
|
|
total = 0
|
|
try:
|
|
for entry in path.iterdir():
|
|
if entry.is_file():
|
|
total += entry.stat().st_size
|
|
except OSError:
|
|
pass
|
|
return total
|
|
|
|
|
|
def read_bag_metadata(bag_dir: Path) -> Optional[dict]:
|
|
"""Returns None if metadata.yaml is missing (e.g. bag was killed mid-write
|
|
before rosbag2 flushed it) or unparseable — a real, if incomplete, state
|
|
the operator should be able to see rather than a crash."""
|
|
meta_path = bag_dir / "metadata.yaml"
|
|
if not meta_path.exists():
|
|
return None
|
|
try:
|
|
with open(meta_path, encoding="utf-8") as f:
|
|
raw = yaml.safe_load(f)
|
|
info = raw["rosbag2_bagfile_information"]
|
|
except Exception:
|
|
return None
|
|
|
|
duration_s = info.get("duration", {}).get("nanoseconds", 0) / 1e9
|
|
message_count = info.get("message_count", 0)
|
|
topics = []
|
|
for entry in info.get("topics_with_message_count", []):
|
|
tm = entry.get("topic_metadata", {})
|
|
topics.append({
|
|
"name": tm.get("name", "?"),
|
|
"type": tm.get("type", "?"),
|
|
"message_count": entry.get("message_count", 0),
|
|
})
|
|
|
|
return {
|
|
"duration_s": round(duration_s, 1),
|
|
"message_count": message_count,
|
|
"topics": topics,
|
|
"size_bytes": dir_size_bytes(bag_dir),
|
|
}
|