Skip to main content

ci2_async/
lib.rs

1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! This crate defines a trait, [AsyncCamera], whose [AsyncCamera::frames]
5//! method returns a [futures::Stream] for asynchronous usage.
6//!
7//! It also provides a struct, [ThreadedAsyncCameraModule] which will take a
8//! camera module implementing the [ci2::CameraModule] trait and wrap it into a
9//! new struct that also implements the [ci2::CameraModule] in addition to
10//! returning a [ThreadedAsyncCamera] which implements the [AsyncCamera] trait.
11//!
12//! For [ThreadedAsyncCameraModule] to work, it requires that the wrapped camera
13//! type `C` implements the [ci2::Camera] and [Send] traits. It operates by
14//! serializing access to the camera by wrapping `Arc<Mutex<C>>`. The
15//! [AsyncCamera::frames] method spawns a thread on on which an infinite loop is
16//! used to grab frames from the camera. Therefore other camera access happens
17//! only between frame acquisitions. Thus, when image exposure times are on the
18//! order of 10 msec, this calls to access the camera (e.g. to chance exposure
19//! time) may block for about 10 msec.
20//!
21//! The structs [ThreadedAsyncCameraModule] and [ThreadedAsyncCamera] here are a
22//! generic implementation that can be used at the cost of spawning a new
23//! thread.
24//!
25//! It would be possible for an upstream camera backend module to directly
26//! implement the [AsyncCamera] trait. Such a camera-specific backend could
27//! implement [AsyncCamera] without serializing access to the camera but rather
28//! by taking advantage of functionality in most camera drivers.
29
30use futures::Stream;
31use tracing::{debug, error};
32
33use machine_vision_formats as formats;
34
35use ci2::{DynamicFrameWithInfo, Result};
36use parking_lot::Mutex;
37use std::sync::Arc;
38
39pub enum FrameResult {
40    Frame(DynamicFrameWithInfo),
41    SingleFrameError(String),
42}
43
44/// Defines a method to return a stream of frames.
45pub trait AsyncCamera {
46    /// asynchronous frame acquisition, get an infinite stream of frames
47    fn frames(
48        &mut self,
49        bufsize: usize,
50    ) -> Result<Box<dyn Stream<Item = FrameResult> + Send + Unpin>>;
51}
52
53pub struct ThreadedAsyncCamera<C> {
54    camera: Arc<Mutex<C>>,
55    name: String,
56    serial: String,
57    model: String,
58    vendor: String,
59    /// When acquiring, has value of Some, else None.
60    control_and_join_handle: Option<(thread_control::Control, std::thread::JoinHandle<()>)>,
61}
62
63fn _test_camera_is_send() {
64    // Compile-time test to ensure ThreadedAsyncCamera implements Send trait.
65    fn implements<T: Send>() {}
66    implements::<ThreadedAsyncCamera<i8>>();
67}
68
69pub struct ThreadedAsyncCameraModule<M, C, G>
70where
71    M: Send,
72    G: Send,
73{
74    cam_module: M,
75    name: String,
76    camera_type: std::marker::PhantomData<C>,
77    guard_type: std::marker::PhantomData<G>,
78}
79
80impl<C: 'static> ThreadedAsyncCamera<C>
81where
82    C: ci2::Camera + Send,
83{
84    pub fn control_and_join_handle(
85        self,
86    ) -> Option<(thread_control::Control, std::thread::JoinHandle<()>)> {
87        self.control_and_join_handle
88    }
89}
90
91impl<C> AsyncCamera for ThreadedAsyncCamera<C>
92where
93    C: 'static + ci2::Camera + Send,
94{
95    fn frames(
96        &mut self,
97        bufsize: usize,
98    ) -> Result<Box<dyn Stream<Item = FrameResult> + Send + Unpin>> {
99        if self.control_and_join_handle.is_some() {
100            return Err(ci2::Error::from("already launched thread"));
101        }
102
103        let (mut tx, rx) = futures::channel::mpsc::channel(bufsize);
104
105        let (flag, control) = thread_control::make_pair();
106
107        let thread_builder =
108            std::thread::Builder::new().name(format!("ThreadedAsyncCamera-{}", self.name));
109        let cam_arc = self.camera.clone();
110        let join_handle: std::thread::JoinHandle<()> = thread_builder.spawn(move || {
111            while flag.is_alive() {
112                // We need to release and re-acquire the lock every cycle to
113                // allow other threads the chance to grab the lock.
114                {
115                    let mut cam = cam_arc.lock();
116                    let msg = match cam.next_frame() {
117                        Ok(frame) => FrameResult::Frame(frame),
118                        Err(ci2::Error::SingleFrameError(s)) => FrameResult::SingleFrameError(s),
119                        Err(e) => {
120                            error!(
121                                "fatal error acquiring frames: {} {:?} {}:{}",
122                                e,
123                                e,
124                                file!(),
125                                line!()
126                            );
127                            return;
128                        }
129                    };
130
131                    match tx.try_send(msg) {
132                        Ok(()) => {} // message put in channel ok
133                        Err(e) => {
134                            if e.is_full() {
135                                // channel was full
136                                error!("dropping message due to backpressure");
137                            }
138                            if e.is_disconnected() {
139                                debug!("ThreadedAsyncCamera listener disconnected");
140                                return;
141                            }
142                        }
143                    };
144                }
145            }
146            debug!(
147                "closing thread {:?} ({:?}) in {}:{}",
148                std::thread::current().name(),
149                std::thread::current().id(),
150                file!(),
151                line!()
152            );
153        })?;
154
155        self.control_and_join_handle = Some((control, join_handle));
156
157        Ok(Box::new(rx))
158    }
159}
160
161impl<M, C, G> ThreadedAsyncCameraModule<M, C, G>
162where
163    M: ci2::CameraModule<CameraType = C, Guard = G>,
164    C: ci2::Camera,
165    G: Send,
166{
167    pub fn threaded_async_camera(&mut self, name: &str) -> Result<ThreadedAsyncCamera<C>> {
168        let camera = self.cam_module.camera(name)?;
169        let name = camera.name().into();
170        let model = camera.name().into();
171        let serial = camera.serial().into();
172        let vendor = camera.vendor().into();
173
174        Ok(ThreadedAsyncCamera {
175            camera: Arc::new(Mutex::new(camera)),
176            name,
177            model,
178            vendor,
179            serial,
180            control_and_join_handle: None,
181        })
182    }
183}
184
185pub fn into_threaded_async<M, C, G>(cam_module: M, _guard: &G) -> ThreadedAsyncCameraModule<M, C, G>
186where
187    M: ci2::CameraModule<CameraType = C, Guard = G>,
188    C: ci2::Camera,
189    G: Send,
190{
191    let name = format!("async-{}", cam_module.name());
192
193    ThreadedAsyncCameraModule {
194        cam_module,
195        name,
196        camera_type: std::marker::PhantomData,
197        guard_type: std::marker::PhantomData,
198    }
199}
200
201// ----
202
203impl<C> ci2::CameraInfo for ThreadedAsyncCamera<C>
204where
205    C: ci2::CameraInfo,
206{
207    fn name(&self) -> &str {
208        &self.name
209    }
210    fn serial(&self) -> &str {
211        &self.serial
212    }
213    fn model(&self) -> &str {
214        &self.model
215    }
216    fn vendor(&self) -> &str {
217        &self.vendor
218    }
219}
220
221impl<C> ci2::Camera for ThreadedAsyncCamera<C>
222where
223    C: ci2::Camera,
224{
225    // ----- start: weakly typed but easier to implement API -----
226
227    // fn feature_access_query(&self, name: &str) -> ci2::Result<ci2::AccessQueryResult> {
228    //     let c = self.camera.lock();
229    //     c.feature_access_query(name)
230    // }
231
232    fn command_execute(&self, name: &str, verify: bool) -> ci2::Result<()> {
233        let c = self.camera.lock();
234        c.command_execute(name, verify)
235    }
236
237    fn feature_bool(&self, name: &str) -> ci2::Result<bool> {
238        let c = self.camera.lock();
239        c.feature_bool(name)
240    }
241
242    fn feature_bool_set(&self, name: &str, value: bool) -> ci2::Result<()> {
243        let c = self.camera.lock();
244        c.feature_bool_set(name, value)
245    }
246
247    fn feature_enum(&self, name: &str) -> ci2::Result<String> {
248        let c = self.camera.lock();
249        c.feature_enum(name)
250    }
251
252    fn feature_enum_set(&self, name: &str, value: &str) -> ci2::Result<()> {
253        let c = self.camera.lock();
254        c.feature_enum_set(name, value)
255    }
256
257    fn feature_float(&self, name: &str) -> ci2::Result<f64> {
258        let c = self.camera.lock();
259        c.feature_float(name)
260    }
261
262    fn feature_float_set(&self, name: &str, value: f64) -> ci2::Result<()> {
263        let c = self.camera.lock();
264        c.feature_float_set(name, value)
265    }
266
267    fn feature_int(&self, name: &str) -> ci2::Result<i64> {
268        let c = self.camera.lock();
269        c.feature_int(name)
270    }
271
272    fn feature_int_set(&self, name: &str, value: i64) -> ci2::Result<()> {
273        let c = self.camera.lock();
274        c.feature_int_set(name, value)
275    }
276
277    // ----- end: weakly typed but easier to implement API -----
278
279    fn node_map_load(&self, settings: &str) -> Result<()> {
280        let c = self.camera.lock();
281        c.node_map_load(settings)
282    }
283    fn node_map_save(&self) -> Result<String> {
284        let c = self.camera.lock();
285        c.node_map_save()
286    }
287
288    fn width(&self) -> ci2::Result<u32> {
289        let c = self.camera.lock();
290        c.width()
291    }
292    fn height(&self) -> ci2::Result<u32> {
293        let c = self.camera.lock();
294        c.height()
295    }
296    fn pixel_format(&self) -> ci2::Result<formats::PixFmt> {
297        let c = self.camera.lock();
298        c.pixel_format()
299    }
300    fn possible_pixel_formats(&self) -> ci2::Result<Vec<formats::PixFmt>> {
301        let c = self.camera.lock();
302        c.possible_pixel_formats()
303    }
304    fn set_pixel_format(&mut self, pixel_format: formats::PixFmt) -> ci2::Result<()> {
305        let mut c = self.camera.lock();
306        c.set_pixel_format(pixel_format)
307    }
308    fn exposure_time(&self) -> ci2::Result<f64> {
309        let c = self.camera.lock();
310        c.exposure_time()
311    }
312    fn exposure_time_range(&self) -> ci2::Result<(f64, f64)> {
313        let c = self.camera.lock();
314        c.exposure_time_range()
315    }
316    fn set_exposure_time(&mut self, value: f64) -> ci2::Result<()> {
317        let mut c = self.camera.lock();
318        c.set_exposure_time(value)
319    }
320    fn gain(&self) -> ci2::Result<f64> {
321        let c = self.camera.lock();
322        c.gain()
323    }
324    fn gain_range(&self) -> ci2::Result<(f64, f64)> {
325        let c = self.camera.lock();
326        c.gain_range()
327    }
328    fn set_gain(&mut self, value: f64) -> ci2::Result<()> {
329        let mut c = self.camera.lock();
330        c.set_gain(value)
331    }
332    fn exposure_auto(&self) -> ci2::Result<ci2::AutoMode> {
333        let c = self.camera.lock();
334        c.exposure_auto()
335    }
336    fn set_exposure_auto(&mut self, value: ci2::AutoMode) -> ci2::Result<()> {
337        let mut c = self.camera.lock();
338        c.set_exposure_auto(value)
339    }
340    fn gain_auto(&self) -> ci2::Result<ci2::AutoMode> {
341        let c = self.camera.lock();
342        c.gain_auto()
343    }
344    fn set_gain_auto(&mut self, value: ci2::AutoMode) -> ci2::Result<()> {
345        let mut c = self.camera.lock();
346        c.set_gain_auto(value)
347    }
348
349    fn start_default_external_triggering(&mut self) -> ci2::Result<()> {
350        let mut c = self.camera.lock();
351        c.start_default_external_triggering()
352    }
353
354    fn set_software_frame_rate_limit(&mut self, fps_limit: f64) -> ci2::Result<()> {
355        let mut c = self.camera.lock();
356        c.set_software_frame_rate_limit(fps_limit)
357    }
358
359    fn trigger_mode(&self) -> ci2::Result<ci2::TriggerMode> {
360        let c = self.camera.lock();
361        c.trigger_mode()
362    }
363    fn set_trigger_mode(&mut self, value: ci2::TriggerMode) -> ci2::Result<()> {
364        let mut c = self.camera.lock();
365        c.set_trigger_mode(value)
366    }
367
368    fn acquisition_frame_rate_enable(&self) -> ci2::Result<bool> {
369        let c = self.camera.lock();
370        c.acquisition_frame_rate_enable()
371    }
372    fn set_acquisition_frame_rate_enable(&mut self, value: bool) -> ci2::Result<()> {
373        let mut c = self.camera.lock();
374        c.set_acquisition_frame_rate_enable(value)
375    }
376
377    fn acquisition_frame_rate(&self) -> ci2::Result<f64> {
378        let c = self.camera.lock();
379        c.acquisition_frame_rate()
380    }
381    fn acquisition_frame_rate_range(&self) -> ci2::Result<(f64, f64)> {
382        let c = self.camera.lock();
383        c.acquisition_frame_rate_range()
384    }
385    fn set_acquisition_frame_rate(&mut self, value: f64) -> ci2::Result<()> {
386        let mut c = self.camera.lock();
387        c.set_acquisition_frame_rate(value)
388    }
389
390    fn trigger_selector(&self) -> ci2::Result<ci2::TriggerSelector> {
391        let c = self.camera.lock();
392        c.trigger_selector()
393    }
394    fn set_trigger_selector(&mut self, value: ci2::TriggerSelector) -> ci2::Result<()> {
395        let mut c = self.camera.lock();
396        c.set_trigger_selector(value)
397    }
398
399    fn acquisition_mode(&self) -> ci2::Result<ci2::AcquisitionMode> {
400        let c = self.camera.lock();
401        c.acquisition_mode()
402    }
403    fn set_acquisition_mode(&mut self, value: ci2::AcquisitionMode) -> ci2::Result<()> {
404        let mut c = self.camera.lock();
405        c.set_acquisition_mode(value)
406    }
407
408    fn acquisition_start(&mut self) -> ci2::Result<()> {
409        let mut c = self.camera.lock();
410        c.acquisition_start()
411    }
412    fn acquisition_stop(&mut self) -> ci2::Result<()> {
413        let mut c = self.camera.lock();
414        c.acquisition_stop()
415    }
416
417    /// blocks forever.
418    fn next_frame(&mut self) -> ci2::Result<DynamicFrameWithInfo> {
419        let mut c = self.camera.lock();
420        c.next_frame()
421    }
422}
423
424impl<M, C, G> ci2::CameraModule for ThreadedAsyncCameraModule<M, C, G>
425where
426    M: ci2::CameraModule<CameraType = C, Guard = G>,
427    C: ci2::Camera,
428    G: Send,
429{
430    type CameraType = C;
431    type Guard = G;
432
433    fn name(&self) -> &str {
434        self.name.as_ref()
435    }
436    fn camera_infos(&self) -> Result<Vec<Box<dyn ci2::CameraInfo>>> {
437        self.cam_module.camera_infos()
438    }
439    fn camera(&mut self, name: &str) -> Result<C> {
440        self.cam_module.camera(name)
441    }
442
443    fn settings_file_extension(&self) -> &str {
444        self.cam_module.settings_file_extension()
445    }
446}