1use std::{cmp::Ordering, pin::Pin};
5
6use futures::{
7 stream::Stream,
8 task::{Context, Poll},
9};
10use pin_project::pin_project;
11
12use crate::FrameDataAndPoints;
13
14use crate::bundled_data::BundledAllCamsOneFrameDistorted;
15use crate::connected_camera_manager::HasCameraList;
16
17#[pin_project]
25pub(crate) struct OrderedLossyFrameBundler<St, HCL>
26where
27 St: Stream<Item = StreamItem>,
28 HCL: HasCameraList,
29{
30 #[pin]
31 stream: St,
32 ccm: HCL,
33 current: Option<BundledAllCamsOneFrameDistorted>,
34 #[pin]
35 pending: Option<StreamItem>,
36}
37
38#[derive(Debug)]
39pub enum StreamItem {
40 EOF,
41 Packet(FrameDataAndPoints),
42}
43
44impl<St, HCL> OrderedLossyFrameBundler<St, HCL>
45where
46 St: Stream<Item = StreamItem>,
47 HCL: HasCameraList,
48{
49 fn new(stream: St, ccm: HCL) -> Self {
50 Self {
51 stream,
52 ccm,
53 current: None,
54 pending: None,
55 }
56 }
57}
58
59impl<St, HCL> Stream for OrderedLossyFrameBundler<St, HCL>
60where
61 St: Stream<Item = StreamItem>,
62 HCL: HasCameraList,
63{
64 type Item = BundledAllCamsOneFrameDistorted;
67 fn poll_next(
68 mut self: Pin<&mut Self>,
69 cx: &mut Context<'_>,
70 ) -> Poll<Option<BundledAllCamsOneFrameDistorted>> {
71 use futures::ready;
72
73 loop {
74 let all_cameras = self.ccm.camera_list();
75
76 let mut this = self.as_mut().project();
77
78 if this.pending.is_none() {
80 let item = match ready!(this.stream.poll_next(cx)) {
81 Some(e) => e,
82 None => {
83 return Poll::Ready(None);
84 }
85 };
86 this.pending.set(Some(item));
87 }
88
89 let new_item: FrameDataAndPoints = match this.pending.take().unwrap() {
91 StreamItem::EOF => {
92 return Poll::Ready(this.current.take());
93 }
94 StreamItem::Packet(new_item) => new_item,
95 };
96
97 if this.current.is_none() {
98 *this.current = Some(BundledAllCamsOneFrameDistorted::new(new_item));
100 } else {
101 let dt = {
103 let current = this.current.as_ref().unwrap();
104 new_item.frame_data.synced_frame.0 as i64 - current.frame().0 as i64
105 };
106
107 match dt.cmp(&0) {
108 Ordering::Equal => {
109 let current: &mut Option<BundledAllCamsOneFrameDistorted> = this.current;
111 let current_cameras = {
112 let x = current.as_mut().unwrap();
113 x.push(new_item);
114 x.cameras()
115 };
116
117 if current_cameras == &all_cameras {
118 let previous = current.take().unwrap();
119 *current = None;
120 return Poll::Ready(Some(previous));
121 }
122 }
123 Ordering::Greater => {
124 let current: &mut Option<BundledAllCamsOneFrameDistorted> = this.current;
128 let previous = current.take().unwrap();
129 *current = Some(BundledAllCamsOneFrameDistorted::new(new_item));
130 return Poll::Ready(Some(previous));
131 }
132 Ordering::Less => {
133 tracing::debug!(
142 "dropping late camera data: cam {} frame {} arrived {} frame(s) \
143 after the current frame and was excluded from live tracking",
144 new_item.frame_data.cam_name.as_str(),
145 new_item.frame_data.synced_frame.0,
146 -dt,
147 );
148 }
149 }
150 }
151 }
152 }
153}
154
155pub(crate) fn bundle_frames<St, HCL>(stream: St, ccm: HCL) -> OrderedLossyFrameBundler<St, HCL>
156where
157 St: Stream<Item = StreamItem>,
158 HCL: HasCameraList,
159{
160 OrderedLossyFrameBundler::new(stream, ccm)
161}
162
163#[test]
164fn test_frame_bundler() {
165 use futures::stream::{self, StreamExt};
166
167 use crate::{FlydraFloatTimestampLocal, FrameData, SyncFno};
168
169 let cam_name_1 = crate::RawCamName::new("cam1".into());
170 let cam_num_1 = crate::CamNum(1);
171 let cam_name_2 = crate::RawCamName::new("cam2".into());
172 let cam_num_2 = crate::CamNum(2);
173 let trigger_timestamp = None;
174
175 let packet1_frame1_cam1 = FrameDataAndPoints {
176 frame_data: FrameData::new(
177 cam_name_1,
178 cam_num_1,
179 SyncFno(1),
180 trigger_timestamp.clone(),
181 FlydraFloatTimestampLocal::from_f64(0.0),
182 None,
183 None,
184 ),
185 points: Vec::new(),
186 };
187
188 let packet2_frame1_cam2 = FrameDataAndPoints {
189 frame_data: FrameData::new(
190 cam_name_2.clone(),
191 cam_num_2,
192 SyncFno(1),
193 trigger_timestamp.clone(),
194 FlydraFloatTimestampLocal::from_f64(0.0),
195 None,
196 None,
197 ),
198 points: Vec::new(),
199 };
200
201 let packet2_frame0_cam2 = FrameDataAndPoints {
202 frame_data: FrameData::new(
203 cam_name_2.clone(),
204 cam_num_2,
205 SyncFno(0),
206 trigger_timestamp.clone(),
207 FlydraFloatTimestampLocal::from_f64(0.0),
208 None,
209 None,
210 ),
211 points: Vec::new(),
212 };
213
214 let packet2_frame2_cam2 = FrameDataAndPoints {
215 frame_data: FrameData::new(
216 cam_name_2.clone(),
217 cam_num_2,
218 SyncFno(2),
219 trigger_timestamp.clone(),
220 FlydraFloatTimestampLocal::from_f64(0.0),
221 None,
222 None,
223 ),
224 points: Vec::new(),
225 };
226
227 let packet2_frame3_cam2 = FrameDataAndPoints {
228 frame_data: FrameData::new(
229 cam_name_2,
230 cam_num_2,
231 SyncFno(3),
232 trigger_timestamp,
233 FlydraFloatTimestampLocal::from_f64(0.0),
234 None,
235 None,
236 ),
237 points: Vec::new(),
238 };
239
240 let inputs: Vec<_> = vec![StreamItem::EOF];
243
244 let cameras = crate::connected_camera_manager::CameraList::new(&[1, 2]);
245 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
246 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
247 assert_eq!(actual.len(), 0);
248
249 let inputs: Vec<_> = vec![
252 StreamItem::Packet(packet1_frame1_cam1.clone()),
253 StreamItem::EOF,
254 ];
255
256 let expected = packet1_frame1_cam1.clone();
257 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
258 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
259 assert_eq!(actual.len(), 1);
260 assert_eq!(
261 actual[0],
262 BundledAllCamsOneFrameDistorted::new(expected.clone())
263 );
264
265 let inputs: Vec<_> = vec![
268 StreamItem::Packet(packet1_frame1_cam1.clone()),
269 StreamItem::Packet(packet2_frame1_cam2.clone()),
270 StreamItem::EOF,
271 ];
272
273 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
274 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
275 assert_eq!(actual.len(), 1);
276 assert_eq!(actual[0].num_cameras(), 2);
277
278 let inputs: Vec<_> = vec![
281 StreamItem::Packet(packet1_frame1_cam1.clone()),
282 StreamItem::Packet(packet2_frame0_cam2),
283 StreamItem::EOF,
284 ];
285
286 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
287 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
288 assert_eq!(actual.len(), 1);
289 assert_eq!(actual[0], BundledAllCamsOneFrameDistorted::new(expected));
290
291 let inputs: Vec<_> = vec![
294 StreamItem::Packet(packet1_frame1_cam1.clone()),
295 StreamItem::Packet(packet2_frame2_cam2),
296 StreamItem::EOF,
297 ];
298
299 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
300 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
301 assert_eq!(actual.len(), 2);
302 assert_eq!(actual[0].num_cameras(), 1);
303 assert_eq!(actual[1].num_cameras(), 1);
304
305 let inputs: Vec<_> = vec![
308 StreamItem::Packet(packet1_frame1_cam1.clone()),
309 StreamItem::Packet(packet2_frame3_cam2),
310 StreamItem::EOF,
311 ];
312
313 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
314 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
315 assert_eq!(actual.len(), 2);
316 assert_eq!(actual[0].num_cameras(), 1);
317 assert_eq!(actual[1].num_cameras(), 1);
318
319 let inputs: Vec<_> = vec![
323 StreamItem::Packet(packet1_frame1_cam1.clone()),
324 StreamItem::Packet(packet2_frame1_cam2),
325 ];
326
327 let bundled = bundle_frames(stream::iter(inputs), cameras.clone());
328 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
329 assert_eq!(actual.len(), 1);
330 assert_eq!(actual[0].num_cameras(), 2);
331
332 let inputs: Vec<_> = vec![StreamItem::Packet(packet1_frame1_cam1)];
335
336 let bundled = bundle_frames(stream::iter(inputs), cameras);
337 let actual: Vec<_> = futures::executor::block_on(bundled.collect());
338 assert_eq!(actual.len(), 0);
339}
340
341#[test]
342fn test_async_stream_ops() {
343 use futures::future;
344 use futures::stream::{self, StreamExt};
345
346 let stream = stream::iter(1..=10);
347 let evens = stream.filter_map(|x| {
348 let ret = if x % 2 == 0 { Some(x + 1) } else { None };
349 future::ready(ret)
350 });
351
352 let result: Vec<_> = futures::executor::block_on(evens.collect());
353 assert_eq!(vec![3, 5, 7, 9, 11], result);
354}