Skip to main content

sightloom_core/
envelope.rs

1//! Portable event envelope shared by index serialization and analytics.
2
3use crate::{ClassId, Direction, EventId, EvidenceRef, FrameStamp, SubjectId, TrackId, ZoneId};
4
5/// Coarse kind tag for an indexed event.
6///
7/// Payload-specific details live in [`EventPayload`]. Hosts match on `kind`
8/// for cheap filtering without decoding full payloads.
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
10pub enum EventKind {
11    /// Zone membership or line crossing.
12    Zone,
13    /// Dwell timing within a zone.
14    Dwell,
15    /// Occupancy change inside a zone.
16    Occupancy,
17    /// Identity / subject resolution lifecycle.
18    Identity,
19    /// Pattern detector output.
20    Pattern,
21    /// Anomaly detector output.
22    Anomaly,
23    /// Application-defined custom event.
24    Custom,
25}
26
27/// Compact payload attached to an [`EventEnvelope`].
28#[derive(Clone, Copy, Debug, PartialEq)]
29pub enum EventPayload {
30    /// No additional fields.
31    Empty,
32    /// Zone enter.
33    Entered {
34        /// Zone that was entered.
35        zone_id: ZoneId,
36        /// Optional class at the time of the event.
37        class_id: Option<ClassId>,
38    },
39    /// Zone exit.
40    Exited {
41        /// Zone that was exited.
42        zone_id: ZoneId,
43        /// Optional class at the time of the event.
44        class_id: Option<ClassId>,
45    },
46    /// Line crossing.
47    Crossed {
48        /// Line zone that was crossed.
49        zone_id: ZoneId,
50        /// Crossing direction.
51        direction: Direction,
52    },
53    /// Dwell started.
54    DwellStarted {
55        /// Zone under dwell.
56        zone_id: ZoneId,
57    },
58    /// Dwell ended.
59    DwellEnded {
60        /// Zone under dwell.
61        zone_id: ZoneId,
62        /// Duration in nanoseconds.
63        duration_ns: i64,
64        /// Visit count after this dwell.
65        visit_count: u32,
66    },
67    /// Occupancy snapshot.
68    Occupancy {
69        /// Zone whose occupancy changed.
70        zone_id: ZoneId,
71        /// Confirmed occupants.
72        occupancy: u32,
73    },
74    /// Free numeric payload for custom / analysis events.
75    Metrics {
76        /// Primary score or magnitude.
77        score: f32,
78        /// Optional secondary value.
79        aux: f32,
80        /// Application tag.
81        tag: u32,
82    },
83}
84
85/// Versioned, queryable event record for a `VisionIndex`.
86///
87/// This is the shared contract between tracking, analytics, storage, and host
88/// products. It does not embed pixels or edit instructions.
89#[derive(Clone, Copy, Debug, PartialEq)]
90pub struct EventEnvelope {
91    /// Stable event id within an index document.
92    pub event_id: EventId,
93    /// Temporal and source stamp for the event.
94    pub stamp: FrameStamp,
95    /// Coarse kind for filtering.
96    pub kind: EventKind,
97    /// Optional track association.
98    pub track_id: Option<TrackId>,
99    /// Optional long-lived subject association.
100    pub subject_id: Option<SubjectId>,
101    /// Optional zone association (also often present in payload).
102    pub zone_id: Option<ZoneId>,
103    /// Optional evidence handle for reels / audit.
104    pub evidence: Option<EvidenceRef>,
105    /// Kind-specific payload.
106    pub payload: EventPayload,
107}
108
109impl EventEnvelope {
110    /// Creates an envelope with empty payload and no optional associations.
111    #[must_use]
112    pub fn new(event_id: EventId, stamp: FrameStamp, kind: EventKind) -> Self {
113        Self {
114            event_id,
115            stamp,
116            kind,
117            track_id: None,
118            subject_id: None,
119            zone_id: None,
120            evidence: None,
121            payload: EventPayload::Empty,
122        }
123    }
124
125    /// Builder-style track association.
126    #[must_use]
127    pub fn with_track(mut self, track_id: TrackId) -> Self {
128        self.track_id = Some(track_id);
129        self
130    }
131
132    /// Builder-style subject association.
133    #[must_use]
134    pub fn with_subject(mut self, subject_id: SubjectId) -> Self {
135        self.subject_id = Some(subject_id);
136        self
137    }
138
139    /// Builder-style zone association.
140    #[must_use]
141    pub fn with_zone(mut self, zone_id: ZoneId) -> Self {
142        self.zone_id = Some(zone_id);
143        self
144    }
145
146    /// Builder-style evidence handle.
147    #[must_use]
148    pub fn with_evidence(mut self, evidence: EvidenceRef) -> Self {
149        self.evidence = Some(evidence);
150        self
151    }
152
153    /// Builder-style payload.
154    #[must_use]
155    pub fn with_payload(mut self, payload: EventPayload) -> Self {
156        self.payload = payload;
157        self
158    }
159}