Files
behavior-tree/robobrain/robot_robobrain/observations.py
T

37 lines
1.9 KiB
Python
Raw Normal View History

from dataclasses import dataclass,asdict
from pathlib import Path
import threading
@dataclass(frozen=True)
class Observation:
observation_id:str
stamp_ns:int
frame_id:str
image_path:str
station_id:str
registry_version:int
shelf_id:str
calibration_id:str=''
geometry_epoch:int=0
def validate(self,goal,now_ns,max_age_ns):
if type(self.stamp_ns) is not int or not goal['capture_after']<=self.stamp_ns<=now_ns or now_ns-self.stamp_ns>max_age_ns:raise ValueError('stale/future observation')
if not self.observation_id or not self.frame_id or not self.image_path:raise ValueError('observation identity incomplete')
if goal.get('observation_station_id',self.station_id)!=self.station_id or goal.get('station_registry_version',self.registry_version)!=self.registry_version:raise ValueError('station/version mismatch')
if goal.get('source_region_ref',self.shelf_id)!=self.shelf_id:raise ValueError('wrong shelf')
if goal.get('expected_geometry_epoch',self.geometry_epoch)!=self.geometry_epoch:raise ValueError('geometry changed')
return asdict(self)
class ObservationCache:
def __init__(self,media_root):self.root=Path(media_root).resolve(strict=True);self.lock=threading.Lock();self.observation=None
def put(self,observation):
path=Path(observation.image_path).resolve(strict=True)
if not path.is_relative_to(self.root) or not path.is_file() or path.stat().st_size>32*1024*1024:raise ValueError('image must be a bounded local trusted media file')
with self.lock:
# Accept a new clock epoch only after consumer freshness check; never
# attach a current timestamp to old image bytes.
self.observation=observation
def get(self):
with self.lock:
if self.observation is None:raise ValueError('no observation')
return self.observation