Made-with: Cursor
3.8 KiB
OutputManager
1. High-Level Overview
Purpose: Handles all persistent output: detection logging (JSON-lines), frame recording (JPEG), health logging, gimbal command logging, and operator detection delivery. Manages NVMe write operations and circular buffer for storage.
Architectural Pattern: Facade over multiple output writers (async file I/O).
Upstream dependencies: Config helper (output paths, recording rates, storage limits), Types helper
Downstream consumers: ScanController
2. Internal Interfaces
Interface: OutputManager
| Method | Input | Output | Async | Error Types |
|---|---|---|---|---|
init(output_dir) |
str | — | No | IOError |
log_detection(entry) |
DetectionLogEntry dict | — | No (non-blocking write) | WriteError |
record_frame(frame, frame_id, level) |
numpy, uint64, int | — | No (non-blocking write) | WriteError |
log_health(health) |
HealthLogEntry dict | — | No | WriteError |
log_gimbal_command(cmd_str) |
str | — | No | WriteError |
report_to_operator(detections) |
list[Detection] | — | No | — |
get_storage_status() |
— | StorageStatus | No | — |
StorageStatus:
nvme_free_pct: float (0-100)
frames_recorded: uint64
detections_logged: uint64
should_reduce_recording: bool — true if free < 20%
4. Data Access Patterns
Storage Estimates
| Output | Write Rate | Per Hour | Per 4h Flight |
|---|---|---|---|
| detections.jsonl | ~1 KB/det, ~100 det/min | ~6 MB | ~24 MB |
| frames/ (L1, 2 FPS) | ~100 KB/frame | ~720 MB | ~2.9 GB |
| frames/ (L2, 30 FPS) | ~100 KB/frame | ~10.8 GB | ~43 GB |
| health.jsonl | ~200 B/s | ~720 KB | ~3 MB |
| gimbal.log | ~500 B/s | ~1.8 MB | ~7 MB |
Circular Buffer Strategy
When NVMe free space < 20%:
- Signal ScanController via
should_reduce_recording - ScanController switches to L1 recording rate only
- If still < 10%: stop L1 frame recording, keep detection log only
- Never overwrite detection logs (most valuable data)
5. Implementation Details
File Writers:
- Detection log: open file handle, append JSON line, flush periodically (every 10 entries or 5s)
- Frame recorder: JPEG encode via OpenCV, write to sequential filename
{frame_id}.jpg - Health log: append JSON line every 1s
- Gimbal log: append text line per command
Operator Delivery: Format detections into existing YOLO output schema (centerX, centerY, width, height, classNum, label, confidence) and make available via the same interface the existing YOLO pipeline uses.
Key Dependencies:
| Library | Version | Purpose |
|---|---|---|
| OpenCV | 4.x | JPEG encoding for frame recording |
| json (stdlib) | — | JSON-lines serialization |
| os (stdlib) | — | NVMe free space check (statvfs) |
Error Handling Strategy:
- WriteError: log to stderr, increment error counter, continue processing (recording failure must not block inference)
- NVMe full: stop recording, log warning, continue detection-only mode
7. Caveats & Edge Cases
Known limitations:
- Frame recording at 30 FPS (L2) writes ~3 MB/s — well within NVMe bandwidth but significant storage consumption
- JSON-lines flush interval means up to 10 detections or 5s of data could be lost on hard crash
8. Dependency Graph
Must be implemented after: Config helper, Types helper Can be implemented in parallel with: Tier1Detector, Tier2SpatialAnalyzer, VLMClient, GimbalDriver Blocks: ScanController (needs OutputManager for logging)
9. Logging Strategy
| Log Level | When | Example |
|---|---|---|
| ERROR | NVMe write failure, disk full | Frame write failed: No space left on device |
| WARN | Storage low, reducing recording | NVMe 18% free, reducing to L1 recording only |
| INFO | Session started, stats | Output session started: /data/output/2026-03-19T14:00/ |