feat(v1): compose temporal fall event pipeline
This commit is contained in:
+22
-2
@@ -23,6 +23,7 @@ class Evidence:
|
||||
class FallEvent:
|
||||
event_id: str
|
||||
track_id: str
|
||||
config_version: str
|
||||
suspected_at_monotonic: float
|
||||
confirmed_at_monotonic: float
|
||||
latency_seconds: float
|
||||
@@ -33,6 +34,7 @@ class FallEvent:
|
||||
class _Record:
|
||||
state: FallState
|
||||
suspect_started_at: Optional[float] = None
|
||||
confirmed_at: Optional[float] = None
|
||||
recovery_started_at: Optional[float] = None
|
||||
last_updated_at: Optional[float] = None
|
||||
|
||||
@@ -40,13 +42,25 @@ class _Record:
|
||||
class FallStateMachine:
|
||||
"""Confirm only uninterrupted, accepted fall evidence for one tracked person."""
|
||||
|
||||
def __init__(self, confirm_window_seconds: float, recovery_window_seconds: float) -> None:
|
||||
def __init__(
|
||||
self,
|
||||
confirm_window_seconds: float,
|
||||
recovery_window_seconds: float,
|
||||
config_version: str,
|
||||
cooldown_seconds: float = 0.0,
|
||||
) -> None:
|
||||
if not 1.0 <= confirm_window_seconds <= 3.0:
|
||||
raise ValueError("confirm_window_seconds must be between 1 and 3 seconds")
|
||||
if recovery_window_seconds <= 0:
|
||||
raise ValueError("recovery_window_seconds must be positive")
|
||||
if cooldown_seconds < 0:
|
||||
raise ValueError("cooldown_seconds must be non-negative")
|
||||
if not isinstance(config_version, str) or not config_version.strip():
|
||||
raise ValueError("config_version must be a non-empty string")
|
||||
self._confirm_window_seconds = float(confirm_window_seconds)
|
||||
self._recovery_window_seconds = float(recovery_window_seconds)
|
||||
self._cooldown_seconds = float(cooldown_seconds)
|
||||
self._config_version = config_version.strip()
|
||||
self._records: Dict[str, _Record] = {}
|
||||
self._next_event_number = 1
|
||||
|
||||
@@ -79,13 +93,17 @@ class FallStateMachine:
|
||||
return []
|
||||
if timestamp - record.suspect_started_at >= self._confirm_window_seconds:
|
||||
record.state = FallState.CONFIRMED
|
||||
record.confirmed_at = timestamp
|
||||
event = self._new_event(track_id, record.suspect_started_at, timestamp)
|
||||
record.suspect_started_at = None
|
||||
return [event]
|
||||
return []
|
||||
|
||||
if record.state is FallState.CONFIRMED:
|
||||
if evidence.is_recovery_candidate:
|
||||
if (
|
||||
evidence.is_recovery_candidate
|
||||
and timestamp - record.confirmed_at >= self._cooldown_seconds
|
||||
):
|
||||
record.state = FallState.RECOVERING
|
||||
record.recovery_started_at = timestamp
|
||||
return []
|
||||
@@ -117,6 +135,7 @@ class FallStateMachine:
|
||||
def _set_normal(record: _Record) -> None:
|
||||
record.state = FallState.NORMAL
|
||||
record.suspect_started_at = None
|
||||
record.confirmed_at = None
|
||||
record.recovery_started_at = None
|
||||
|
||||
def _new_event(
|
||||
@@ -125,6 +144,7 @@ class FallStateMachine:
|
||||
event = FallEvent(
|
||||
event_id="FALL-{0:06d}".format(self._next_event_number),
|
||||
track_id=track_id,
|
||||
config_version=self._config_version,
|
||||
suspected_at_monotonic=suspected_at,
|
||||
confirmed_at_monotonic=confirmed_at,
|
||||
latency_seconds=confirmed_at - suspected_at,
|
||||
|
||||
Reference in New Issue
Block a user