1use std::collections::{BTreeMap, BTreeSet};
5use std::sync::{
6 Arc, Mutex, RwLock,
7 atomic::{AtomicBool, Ordering},
8};
9use tracing::{debug, error, info};
10
11use crate::{CamInfoRow, MyFloat, safe_u8};
12use braid_types::{
13 BuiServerInfo, CamInfo, CamNum, ConnectedCameraSyncState, PtpStamp, PtpSyncConfig, RawCamName,
14 RecentStats, SyncFno, TRIGGERBOX_SYNC_SECONDS, TriggerType,
15};
16
17#[derive(Debug, Clone, Copy)]
19pub struct SyncStart {
20 pub instant: std::time::Instant,
22 pub wallclock: chrono::DateTime<chrono::Utc>,
26}
27
28impl SyncStart {
29 pub fn now() -> Self {
30 Self {
31 instant: std::time::Instant::now(),
32 wallclock: chrono::Utc::now(),
33 }
34 }
35}
36
37pub(crate) trait HasCameraList {
38 fn camera_list(&self) -> CameraList;
39}
40
41#[derive(Clone, Debug, PartialEq)]
46pub(crate) struct CameraList {
47 pub(crate) inner: BTreeSet<u8>,
48}
49
50impl CameraList {
51 pub(crate) fn new(cams: &[u8]) -> Self {
52 let inner = cams.iter().copied().collect();
53 Self { inner }
54 }
55}
56
57impl HasCameraList for CameraList {
58 fn camera_list(&self) -> CameraList {
59 self.clone()
60 }
61}
62
63#[derive(Debug)]
64pub struct ConnectedCameraInfo {
65 cam_num: CamNum,
66 raw_cam_name: RawCamName,
67 sync_state: ConnectedCameraSyncState,
68 http_camserver_info: BuiServerInfo,
69 frames_during_sync: u64,
70 _camera_periodic_signal_period_usec: Option<f64>,
71}
72
73impl ConnectedCameraInfo {
74 fn copy_to_caminfo(&self) -> CamInfoRow {
75 CamInfoRow {
76 camn: self.cam_num,
77 cam_id: self.raw_cam_name.as_str().to_string(),
78 }
79 }
80}
81
82#[derive(Debug)]
83struct ConnectedCamerasManagerInner {
84 all_expected_cameras: BTreeSet<RawCamName>,
85 next_cam_num: CamNum,
86 ccis: BTreeMap<RawCamName, ConnectedCameraInfo>,
87 not_yet_connected: BTreeMap<RawCamName, CamNum>,
88 all_expected_cameras_are_present: bool,
89 all_expected_cameras_are_synced: bool,
90 first_frame_arrived: BTreeSet<RawCamName>,
91}
92
93pub trait ConnectedCamCallback: Send {
94 fn on_cam_changed(&self, _: Vec<CamInfo>);
95}
96
97#[derive(Clone)]
103pub struct ConnectedCamerasManager {
104 inner: Arc<RwLock<ConnectedCamerasManagerInner>>,
105 on_cam_change_func: Arc<Mutex<Option<Box<dyn ConnectedCamCallback>>>>,
106 recon: Option<flydra_mvg::FlydraMultiCameraSystem<MyFloat>>,
107 signal_all_cams_present: Arc<AtomicBool>,
108 signal_all_cams_synced: Arc<AtomicBool>,
109 launch_time_ptp: PtpStamp,
110 periodic_signal_period_usec: Option<f64>,
111}
112
113impl HasCameraList for ConnectedCamerasManager {
114 fn camera_list(&self) -> CameraList {
115 let inner: BTreeSet<u8> = self
116 .inner
117 .read()
118 .unwrap()
119 .ccis
120 .values()
121 .map(|cci| cci.cam_num.0)
122 .collect();
123 CameraList { inner }
124 }
125}
126
127impl ConnectedCamerasManager {
128 pub fn new(
129 recon: &Option<flydra_mvg::FlydraMultiCameraSystem<MyFloat>>,
130 all_expected_cameras: BTreeSet<RawCamName>,
131 signal_all_cams_present: Arc<AtomicBool>,
132 signal_all_cams_synced: Arc<AtomicBool>,
133 periodic_signal_period_usec: Option<f64>,
134 predefined_cam_nums: Option<BTreeMap<RawCamName, CamNum>>,
135 ) -> Self {
136 let mut not_yet_connected = BTreeMap::new();
137
138 let next_cam_num = if let Some(recon) = recon {
140 for (base_num, cam_name) in recon.cam_names().enumerate() {
141 let raw_cam_name = RawCamName::new(cam_name.to_string());
142 if let Some(predefined_cam_nums) = &predefined_cam_nums
143 && let Some(predefined_cam_num) = predefined_cam_nums.get(&raw_cam_name)
144 {
145 not_yet_connected.insert(raw_cam_name, *predefined_cam_num);
146 } else {
147 let cam_num: CamNum = safe_u8(base_num).into();
148 not_yet_connected.insert(raw_cam_name, cam_num);
149 }
150 }
151 safe_u8(recon.len())
152 } else {
153 0
154 };
155
156 let launch_time = chrono::Utc::now();
157 let mut launch_time_ptp = PtpStamp::try_from(launch_time).unwrap();
158
159 if let Some(periodic_signal_period_usec) = periodic_signal_period_usec.as_ref() {
160 let periodic_signal_period_nsec = (periodic_signal_period_usec * 1000.0) as u64;
167 let n_ticks = launch_time_ptp.get() / periodic_signal_period_nsec - 1;
168 launch_time_ptp = PtpStamp::new(n_ticks * periodic_signal_period_nsec);
169 }
170
171 let launch_time_ptp_utc: chrono::DateTime<chrono::Utc> =
172 launch_time_ptp.clone().try_into().unwrap();
173 let launch_time_ptp_local: chrono::DateTime<chrono::Local> = launch_time_ptp_utc.into();
174 tracing::debug!("launch_time_ptp_local: {launch_time_ptp_local}");
175
176 Self {
177 signal_all_cams_present,
178 signal_all_cams_synced,
179 inner: Arc::new(RwLock::new(ConnectedCamerasManagerInner {
180 all_expected_cameras,
181 next_cam_num: next_cam_num.into(),
182 ccis: BTreeMap::new(),
183 not_yet_connected,
184 all_expected_cameras_are_present: false,
185 all_expected_cameras_are_synced: false,
186 first_frame_arrived: BTreeSet::new(),
187 })),
188 on_cam_change_func: Arc::new(Mutex::new(None)),
189 recon: recon.clone(),
190 launch_time_ptp,
191 periodic_signal_period_usec,
192 }
193 }
194
195 pub fn reset_sync_data(&mut self) {
197 info!("Camera manager dropping old cameras and expecting new cameras");
198
199 let mut next_cam_num = { self.inner.read().unwrap().next_cam_num.0 };
200 let mut not_yet_connected = BTreeMap::new();
201
202 if let Some(recon) = &self.recon {
204 for cam_name in recon.cam_names() {
205 let cam_num = next_cam_num;
206 next_cam_num = safe_u8(next_cam_num as usize + 1);
207 let raw_cam_name = RawCamName::new(cam_name.to_string());
208 let cam_num: CamNum = cam_num.into();
209 not_yet_connected.insert(raw_cam_name, cam_num);
210 }
211 }
212
213 let old_ccis = {
214 let mut inner = self.inner.write().unwrap();
215 inner.next_cam_num = next_cam_num.into();
216 let old_ccis = std::mem::take(&mut inner.ccis);
217 inner.not_yet_connected = not_yet_connected;
218 old_ccis
219 };
220
221 for cam_info in old_ccis.values() {
222 self.register_new_camera(
224 &cam_info.raw_cam_name,
225 &cam_info.http_camserver_info,
226 self.periodic_signal_period_usec,
227 )
228 .unwrap();
229 }
230 }
231
232 pub fn set_cam_changed_callback(
234 &mut self,
235 f: Box<dyn ConnectedCamCallback>,
236 ) -> Option<Box<dyn ConnectedCamCallback>> {
237 let old = {
238 let mut mutex_guard = self.on_cam_change_func.lock().unwrap();
239 mutex_guard.replace(f)
240 };
241
242 self.notify_cam_changed_listeners();
244 old
245 }
246
247 fn notify_cam_changed_listeners(&self) {
248 let mutex_guard = self.on_cam_change_func.lock().unwrap();
249 let inner_ref: Option<&Box<dyn ConnectedCamCallback>> = mutex_guard.as_ref();
250 if let Some(cb) = inner_ref {
251 let cams = {
252 self.inner
254 .read()
255 .unwrap()
256 .ccis
257 .values()
258 .map(|cci| CamInfo {
259 name: cci.raw_cam_name.clone(),
260 state: cci.sync_state.clone(),
261 strand_cam_http_server_info: cci.http_camserver_info.clone(),
262 recent_stats: RecentStats::default(),
263 })
264 .collect()
265 };
266 cb.on_cam_changed(cams)
267 }
268 }
269
270 pub fn new_single_cam(
275 raw_cam_name: &RawCamName,
276 http_camserver_info: &BuiServerInfo,
277 recon: &Option<flydra_mvg::FlydraMultiCameraSystem<MyFloat>>,
278 camera_periodic_signal_period_usec: Option<f64>,
279 ) -> Self {
280 let signal_all_cams_present = Arc::new(AtomicBool::new(false));
281 let signal_all_cams_synced = Arc::new(AtomicBool::new(false));
282
283 let mut all_expected_cameras = BTreeSet::new();
284 all_expected_cameras.insert(raw_cam_name.clone());
285
286 let this = Self::new(
287 recon,
288 all_expected_cameras,
289 signal_all_cams_present,
290 signal_all_cams_synced,
291 camera_periodic_signal_period_usec,
292 None,
293 );
294 {
295 let raw_cam_name = raw_cam_name.clone();
296
297 let mut inner = this.inner.write().unwrap();
298
299 assert!(
300 !inner.ccis.contains_key(&raw_cam_name),
301 "camera connecting again?"
302 );
303
304 let cam_num = if let Some(pre_existing) = inner.not_yet_connected.remove(&raw_cam_name)
305 {
306 debug!(
307 "registering camera {}, which is in existing calibration",
308 raw_cam_name.as_str()
309 );
310 pre_existing
311 } else {
312 debug!(
313 "registering camera {}, which is not in existing calibration",
314 raw_cam_name.as_str()
315 );
316 let cam_num = inner.next_cam_num;
318 inner.next_cam_num.0 += 1;
319 cam_num
320 };
321
322 inner.ccis.insert(
323 raw_cam_name.clone(),
324 ConnectedCameraInfo {
325 cam_num,
326 raw_cam_name,
327 sync_state: ConnectedCameraSyncState::Unsynchronized,
328 http_camserver_info: http_camserver_info.clone(),
329 frames_during_sync: 0,
330 _camera_periodic_signal_period_usec: camera_periodic_signal_period_usec,
331 },
332 );
333 }
334 this
335 }
336
337 pub fn remove(&mut self, raw_cam_name: &RawCamName) {
338 self.inner.write().unwrap().ccis.remove(raw_cam_name);
339 self.notify_cam_changed_listeners();
340 }
341
342 pub fn register_new_camera(
347 &mut self,
348 raw_cam_name: &RawCamName,
349 http_camserver_info: &BuiServerInfo,
350 camera_periodic_signal_period_usec: Option<f64>,
351 ) -> Result<(), &'static str> {
352 if camera_periodic_signal_period_usec != self.periodic_signal_period_usec {
353 return Err(
354 "camera_periodic_signal_period_usec differs from periodic_signal_period_usec.",
355 );
356 }
357 let raw_cam_name = raw_cam_name.clone();
358 let cam_num = {
359 let mut inner = self.inner.write().unwrap();
361
362 if inner.ccis.contains_key(&raw_cam_name) {
363 tracing::error!(
364 "Camera \"{raw_cam_name}\" has already connected but is attempting to connect again."
365 );
366 return Err("camera already connected");
367 }
368
369 let cam_num = if let Some(pre_existing) = inner.not_yet_connected.remove(&raw_cam_name)
370 {
371 debug!(
372 "registering camera {}, which is in existing calibration",
373 raw_cam_name.as_str()
374 );
375 pre_existing
376 } else {
377 if self.recon.is_some() {
378 tracing::warn!(
379 "Camera {} connected, but this is not in existing calibration.",
380 raw_cam_name.as_str()
381 );
382 }
383 let cam_num_inner = inner.next_cam_num;
385 inner.next_cam_num.0 += 1;
386 cam_num_inner
387 };
388
389 inner.ccis.insert(
390 raw_cam_name.clone(),
391 ConnectedCameraInfo {
392 cam_num,
393 raw_cam_name: raw_cam_name.clone(),
394 sync_state: ConnectedCameraSyncState::Unsynchronized,
395 http_camserver_info: http_camserver_info.clone(),
396 frames_during_sync: 0,
397 _camera_periodic_signal_period_usec: camera_periodic_signal_period_usec,
398 },
399 );
400 cam_num
401 };
402 info!(
403 "register_new_camera got camera name \"{}\", \
404 assigned camera number {}",
405 raw_cam_name.as_str(),
406 cam_num
407 );
408 self.notify_cam_changed_listeners();
409 Ok(())
410 }
411
412 pub fn got_new_frame_live<F>(
416 &self,
417 packet: &braid_types::FlydraRawUdpPacket,
418 sync_pulse_pause_started_arc: &Arc<RwLock<Option<SyncStart>>>,
419 send_new_frame_offset: F,
420 trigger_cfg: &TriggerType,
421 ) -> Option<SyncFno>
422 where
423 F: FnMut(u64),
424 {
425 let sync_data = match &trigger_cfg {
426 TriggerType::TriggerboxV1(_) => self.got_new_frame_live_triggerbox(
427 packet,
428 sync_pulse_pause_started_arc,
429 TRIGGERBOX_SYNC_SECONDS,
430 None,
431 ),
432 TriggerType::FakeSync(cfg) => self.got_new_frame_live_triggerbox(
433 packet,
434 sync_pulse_pause_started_arc,
435 0,
436 Some(cfg.framerate),
437 ),
438 TriggerType::PtpSync(ptpcfg) => self.got_new_frame_live_ptp(packet, ptpcfg)?,
439 TriggerType::DeviceTimestamp => {
440 todo!();
441 }
442 };
443 self.finish_got_new_frame_live(sync_data, send_new_frame_offset)
444 }
445
446 fn got_new_frame_live_triggerbox(
459 &self,
460 packet: &braid_types::FlydraRawUdpPacket,
461 sync_pulse_pause_started_arc: &Arc<RwLock<Option<SyncStart>>>,
462 sync_time_min_sec: u64,
463 fake_sync_fps: Option<f64>,
464 ) -> SyncData {
465 assert!(packet.framenumber >= 0);
466
467 let sync_time_min: std::time::Duration = std::time::Duration::from_secs(sync_time_min_sec);
468 let sync_time_max = std::time::Duration::from_secs(TRIGGERBOX_SYNC_SECONDS + 2);
469
470 let raw_cam_name = RawCamName::new(packet.cam_name.clone());
471
472 let cam_frame = packet.framenumber as u64;
473 let mut synced_frame = None;
474 let mut new_frame0 = None;
475 let mut got_frame_during_sync_time = false;
476 let mut do_check_if_all_cameras_present = false;
477 {
478 let inner = self.inner.read().unwrap();
479 if let Some(cci) = inner.ccis.get(&raw_cam_name) {
480 use crate::ConnectedCameraSyncState::*;
482 match cci.sync_state {
483 Unsynchronized => {
484 do_check_if_all_cameras_present = true;
485 let sync_pulse_pause_started = sync_pulse_pause_started_arc.read().unwrap();
486 if let Some(sync_start) = *sync_pulse_pause_started {
487 let elapsed = sync_start.instant.elapsed();
488 if sync_time_min < elapsed && elapsed < sync_time_max {
489 match fake_sync_fps {
490 None => {
491 new_frame0 =
495 Some(cam_frame - crate::TRIGGERBOX_FIRST_PULSE);
496
497 synced_frame = Some(crate::TRIGGERBOX_FIRST_PULSE);
499 }
500 Some(fps) => {
501 let elapsed_wall = packet.cam_received_time.as_f64()
507 - strand_datetime_conversion::datetime_to_f64(
508 &sync_start.wallclock,
509 );
510 let k = (elapsed_wall * fps).round();
511 let k_max = (sync_time_max.as_secs_f64() * fps).ceil();
512 if (0.0..=k_max).contains(&k) {
513 let this_synced =
514 k as u64 + crate::TRIGGERBOX_FIRST_PULSE;
515 if let Some(frame0) = cam_frame.checked_sub(this_synced)
516 {
517 new_frame0 = Some(frame0);
518 synced_frame = Some(this_synced);
519 }
520 } else if k > k_max {
524 tracing::warn!(
531 "fake sync: frame timestamp of camera \"{}\" is \
532 {elapsed_wall:.3} s from the sync epoch; falling \
533 back to arrival-order synchronization. Are the \
534 host clocks synchronized?",
535 raw_cam_name.as_str()
536 );
537 new_frame0 =
538 Some(cam_frame - crate::TRIGGERBOX_FIRST_PULSE);
539 synced_frame = Some(crate::TRIGGERBOX_FIRST_PULSE);
540 }
541 }
545 }
546 } else if std::time::Duration::from_millis(50) < elapsed {
547 got_frame_during_sync_time = true;
551 }
552 }
553 }
554 Synchronized(frame0) => {
555 if cam_frame >= frame0 {
556 let corrected_frame_number = cam_frame - frame0;
558
559 if corrected_frame_number == u64::MAX {
561 panic!(
569 "Impossible frame number. cam_name: {}, cam_frame: {}, frame0: {}",
570 raw_cam_name.as_str(),
571 cam_frame,
572 frame0,
573 );
574 }
575 synced_frame = Some(corrected_frame_number);
579 }
580 }
581 };
582 }
583 }
587
588 if got_frame_during_sync_time {
589 let frames_during_sync = {
590 let mut inner = self.inner.write().unwrap();
592
593 match inner.ccis.get_mut(&raw_cam_name) {
594 Some(cci) => {
595 cci.frames_during_sync += 1;
596 cci.frames_during_sync
597 }
598 None => {
599 panic!("reached impossible code.");
600 }
601 }
602 };
603
604 if frames_during_sync > 10 {
605 error!(
606 "Many frames during sync period. Camera \"{}\" not \
607 being externally triggered?",
608 raw_cam_name.as_str()
609 );
610 }
611 }
612 SyncData {
613 new_frame0,
614 raw_cam_name,
615 do_check_if_all_cameras_present,
616 synced_frame,
617 }
618 }
619
620 fn got_new_frame_live_ptp(
622 &self,
623 packet: &braid_types::FlydraRawUdpPacket,
624 ptpcfg: &PtpSyncConfig,
625 ) -> Option<SyncData> {
626 let raw_cam_name = RawCamName::new(packet.cam_name.clone());
627 let cam = raw_cam_name.as_str();
628 let my_span = tracing::span!(tracing::Level::DEBUG, "got_new_frame_live_ptp", cam);
629 let _enter = my_span.enter();
630
631 let inner = self.inner.read().unwrap();
632 if let Some(cci) = inner.ccis.get(&raw_cam_name) {
633 let camera_periodic_signal_period_usec = self
634 .periodic_signal_period_usec
635 .expect("could not get period for PTP sync");
636 if let Some(expected_period) = ptpcfg.periodic_signal_period_usec
637 && approx::relative_ne!(expected_period, camera_periodic_signal_period_usec)
638 {
639 panic!("camera period not set to expected period");
640 }
641 let device_timestamp = PtpStamp::new(
642 packet
643 .device_timestamp
644 .expect("could not get device_timestamp for frame"),
645 );
646
647 let device_timestamp_utc: chrono::DateTime<chrono::Utc> =
648 device_timestamp.clone().try_into().unwrap();
649 let device_timestamp_local: chrono::DateTime<chrono::Local> =
650 device_timestamp_utc.into();
651 tracing::trace!("{cam}: device_timestamp_local: {device_timestamp_local}");
652
653 let elapsed_since_launch = if let Some(dur) =
654 device_timestamp.duration_since(&self.launch_time_ptp)
655 {
656 dur
657 } else {
658 tracing::warn!(
659 "Launch time precedes device timestamp {raw_cam_name}. Is time running backwards?"
660 );
661 return None;
664 };
665
666 let camera_periodic_signal_period_nsec = camera_periodic_signal_period_usec * 1000.0;
667 let n_periods =
668 elapsed_since_launch.nanos() as f64 / camera_periodic_signal_period_nsec;
669 let raw_fno = n_periods.round() as u64;
670 let device_timestamp_value = device_timestamp.get();
671 tracing::trace!(device_timestamp_value, n_periods, raw_fno);
672 tracing::trace!(
673 "packet.block_id: {:?}, packet.framenumber: {:?}, launch_time_ptp: {:?}",
674 packet.block_id,
675 packet.framenumber,
676 self.launch_time_ptp
677 );
678 tracing::trace!(
679 "elapsed_since_launch: {elapsed_since_launch:?}, camera_periodic_signal_period_nsec: {camera_periodic_signal_period_nsec}, raw_fno: {raw_fno}"
680 );
681
682 let mut do_check_if_all_cameras_present = false;
683
684 let synced_frame = Some(raw_fno);
685 let mut new_frame0 = None;
686 use crate::ConnectedCameraSyncState::*;
687 match &cci.sync_state {
688 Unsynchronized => {
689 new_frame0 = Some(0);
690 do_check_if_all_cameras_present = true;
691 }
692 Synchronized(_frame0) => {}
693 }
694
695 Some(SyncData {
696 new_frame0,
697 raw_cam_name,
698 do_check_if_all_cameras_present,
699 synced_frame,
700 })
701 } else {
702 None
704 }
705 }
706
707 fn finish_got_new_frame_live<F>(
708 &self,
709 sync_data: SyncData,
710 mut send_new_frame_offset: F,
711 ) -> Option<SyncFno>
712 where
713 F: FnMut(u64),
714 {
715 let SyncData {
716 new_frame0,
717 raw_cam_name,
718 do_check_if_all_cameras_present,
719 synced_frame,
720 } = sync_data;
721 let mut do_check_if_all_cameras_synchronized = false;
722 if let Some(frame0) = new_frame0 {
723 {
725 let mut inner = self.inner.write().unwrap();
727 match inner.ccis.get_mut(&raw_cam_name) {
728 Some(cci) => {
729 cci.sync_state = ConnectedCameraSyncState::Synchronized(frame0);
730 }
731 None => {
732 panic!("reached impossible code.");
733 }
734 }
735 }
736
737 self.notify_cam_changed_listeners();
738
739 send_new_frame_offset(frame0);
741 info!(
742 "cam \"{}\" synchronized with frame offset: {}",
743 raw_cam_name.as_str(),
744 frame0,
745 );
746 do_check_if_all_cameras_synchronized = true;
747 }
748
749 if do_check_if_all_cameras_present
750 && !self.inner.read().unwrap().all_expected_cameras_are_present
751 {
752 let mut inner = self.inner.write().unwrap();
753 let i2: &mut ConnectedCamerasManagerInner = &mut inner;
754 if i2.first_frame_arrived.insert(raw_cam_name.clone()) {
755 info!(
756 "first frame from camera \"{}\" arrived.",
757 raw_cam_name.as_str()
758 );
759 if i2.first_frame_arrived == i2.all_expected_cameras {
760 inner.all_expected_cameras_are_present = true;
761 self.signal_all_cams_present.store(true, Ordering::SeqCst);
762 info!("All expected cameras connected.");
763 } else {
764 info!("All expected cameras NOT connected.");
765 }
766 }
767 }
768
769 if do_check_if_all_cameras_synchronized
770 && !self.inner.read().unwrap().all_expected_cameras_are_synced
771 {
772 let mut inner = self.inner.write().unwrap();
773 let i2: &mut ConnectedCamerasManagerInner = &mut inner;
774 let mut all_synced = true;
777 for raw_cam_name in i2.all_expected_cameras.iter() {
778 let this_sync = i2
779 .ccis
780 .get(raw_cam_name)
781 .map(|cci| cci.sync_state.is_synchronized())
782 .unwrap_or(false);
783 if !this_sync {
784 all_synced = false;
785 break;
786 }
787 }
788
789 if all_synced {
790 info!("All expected cameras synchronized.");
791 self.signal_all_cams_synced.store(true, Ordering::SeqCst);
792 } else {
793 info!("All expected cameras NOT synchronized.");
794 }
795 }
796
797 synced_frame.map(SyncFno)
798 }
799
800 pub fn get_raw_cam_name(&self, cam_num: CamNum) -> Option<RawCamName> {
801 for cci in self.inner.read().unwrap().ccis.values() {
802 if cci.cam_num == cam_num {
803 return Some(cci.raw_cam_name.clone());
804 }
805 }
806 None
807 }
808
809 pub fn all_raw_cam_names(&self) -> Vec<RawCamName> {
810 self.inner
811 .read()
812 .unwrap()
813 .ccis
814 .values()
815 .map(|cci| cci.raw_cam_name.clone())
816 .collect()
817 }
818
819 pub fn http_camserver_info(&self, raw_cam_name: &RawCamName) -> Option<BuiServerInfo> {
820 self.inner
821 .read()
822 .unwrap()
823 .ccis
824 .get(raw_cam_name)
825 .map(|cci| cci.http_camserver_info.clone())
826 }
827
828 pub fn cam_num(&self, raw_cam_name: &RawCamName) -> Option<CamNum> {
829 let inner = self.inner.read().unwrap();
830 match inner.ccis.get(raw_cam_name) {
831 Some(cci) => Some(cci.cam_num),
832 None => inner.not_yet_connected.get(raw_cam_name).copied(),
833 }
834 }
835
836 pub(crate) fn sample(&self) -> Vec<CamInfoRow> {
837 self.inner
838 .read()
839 .unwrap()
840 .ccis
841 .values()
842 .map(|cci| cci.copy_to_caminfo())
843 .collect()
844 }
845
846 pub fn len(&self) -> usize {
847 self.inner.read().unwrap().ccis.len()
848 }
849
850 pub fn is_empty(&self) -> bool {
851 self.inner.read().unwrap().ccis.is_empty()
852 }
853}
854
855impl std::fmt::Debug for ConnectedCamerasManager {
856 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
857 f.debug_struct("ConnectedCamerasManager")
858 .finish_non_exhaustive()
859 }
860}
861
862#[derive(Debug)]
863struct SyncData {
864 new_frame0: Option<u64>,
865 raw_cam_name: RawCamName,
866 do_check_if_all_cameras_present: bool,
867 synced_frame: Option<u64>,
868}
869
870#[test]
871fn test_camera_list() {
872 let c1 = CameraList::new(&[1, 2, 3, 4]);
873 let c2 = CameraList::new(&[4, 3, 2, 1]);
874 assert_eq!(c1, c2);
875
876 let c1 = CameraList::new(&[1, 2, 3, 4]);
877 let c2 = CameraList::new(&[4, 3, 2]);
878 assert!(c1 != c2);
879
880 let c1 = CameraList::new(&[1, 2, 3, 4]);
881 let c2 = CameraList::new(&[4, 3, 2, 5]);
882 assert!(c1 != c2);
883}