synheart_sensor_agent/core/
windowing.rs1use crate::collector::types::{KeyboardEvent, MouseEvent, SensorEvent};
7use chrono::{DateTime, Duration, Utc};
8use serde::{Deserialize, Serialize};
9
10#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct EventWindow {
13 pub start: DateTime<Utc>,
15 pub end: DateTime<Utc>,
17 pub keyboard_events: Vec<KeyboardEvent>,
19 pub mouse_events: Vec<MouseEvent>,
21 pub is_session_start: bool,
23}
24
25impl EventWindow {
26 pub fn new(start: DateTime<Utc>, duration: Duration) -> Self {
28 Self {
29 start,
30 end: start + duration,
31 keyboard_events: Vec::new(),
32 mouse_events: Vec::new(),
33 is_session_start: false,
34 }
35 }
36
37 pub fn contains(&self, timestamp: DateTime<Utc>) -> bool {
39 timestamp >= self.start && timestamp < self.end
40 }
41
42 pub fn add_event(&mut self, event: SensorEvent) {
44 match event {
45 SensorEvent::Keyboard(e) => self.keyboard_events.push(e),
46 SensorEvent::Mouse(e) => self.mouse_events.push(e),
47 }
48 }
49
50 pub fn is_empty(&self) -> bool {
52 self.keyboard_events.is_empty() && self.mouse_events.is_empty()
53 }
54
55 pub fn event_count(&self) -> usize {
57 self.keyboard_events.len() + self.mouse_events.len()
58 }
59
60 pub fn duration_secs(&self) -> f64 {
62 (self.end - self.start).num_milliseconds() as f64 / 1000.0
63 }
64}
65
66pub struct WindowManager {
68 window_duration: Duration,
70 session_gap_threshold: Duration,
72 current_window: Option<EventWindow>,
74 completed_windows: Vec<EventWindow>,
76 last_event_time: Option<DateTime<Utc>>,
78}
79
80impl WindowManager {
81 pub fn new(window_duration_secs: u64, session_gap_threshold_secs: u64) -> Self {
83 Self {
84 window_duration: Duration::seconds(window_duration_secs as i64),
85 session_gap_threshold: Duration::seconds(session_gap_threshold_secs as i64),
86 current_window: None,
87 completed_windows: Vec::new(),
88 last_event_time: None,
89 }
90 }
91
92 pub fn process_event(&mut self, event: SensorEvent) {
99 let event_time = event.timestamp();
100
101 let is_new_session = if let Some(last_time) = self.last_event_time {
103 event_time - last_time > self.session_gap_threshold
104 } else {
105 true };
107
108 if is_new_session && self.current_window.is_some() {
110 self.complete_current_window();
111 }
112
113 if self.current_window.is_none() {
115 let mut window = EventWindow::new(event_time, self.window_duration);
116 window.is_session_start = is_new_session;
117 self.current_window = Some(window);
118 }
119
120 let window = self.current_window.as_ref().unwrap();
122 if event_time >= window.end {
123 self.complete_current_window();
125
126 let mut window = EventWindow::new(event_time, self.window_duration);
128 window.is_session_start = is_new_session;
129 self.current_window = Some(window);
130 }
131
132 if let Some(ref mut window) = self.current_window {
134 window.add_event(event);
135 }
136
137 self.last_event_time = Some(event_time);
138 }
139
140 pub fn flush(&mut self) {
142 self.complete_current_window();
143 }
144
145 pub fn take_completed_windows(&mut self) -> Vec<EventWindow> {
147 std::mem::take(&mut self.completed_windows)
148 }
149
150 pub fn has_completed_windows(&self) -> bool {
152 !self.completed_windows.is_empty()
153 }
154
155 pub fn completed_window_count(&self) -> usize {
157 self.completed_windows.len()
158 }
159
160 fn complete_current_window(&mut self) {
162 if let Some(window) = self.current_window.take() {
163 if !window.is_empty() {
165 self.completed_windows.push(window);
166 }
167 }
168 }
169
170 pub fn check_window_expiry(&mut self) {
172 let now = Utc::now();
173 if let Some(ref window) = self.current_window {
174 if now >= window.end {
175 self.complete_current_window();
176 }
177 }
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_window_creation() {
187 let start = Utc::now();
188 let window = EventWindow::new(start, Duration::seconds(10));
189
190 assert_eq!(window.start, start);
191 assert_eq!(window.end, start + Duration::seconds(10));
192 assert!(window.is_empty());
193 }
194
195 #[test]
196 fn test_window_contains() {
197 let start = Utc::now();
198 let window = EventWindow::new(start, Duration::seconds(10));
199
200 assert!(window.contains(start));
201 assert!(window.contains(start + Duration::seconds(5)));
202 assert!(!window.contains(start + Duration::seconds(10)));
203 assert!(!window.contains(start - Duration::seconds(1)));
204 }
205
206 #[test]
207 fn test_window_manager_basic() {
208 let mut manager = WindowManager::new(10, 300);
209
210 for _ in 0..5 {
212 let event = SensorEvent::Keyboard(crate::collector::types::KeyboardEvent::new(true));
213 manager.process_event(event);
214 }
215
216 assert!(!manager.has_completed_windows());
218
219 manager.flush();
221 assert!(manager.has_completed_windows());
222
223 let windows = manager.take_completed_windows();
224 assert_eq!(windows.len(), 1);
225 assert_eq!(windows[0].keyboard_events.len(), 5);
226 }
227}