Skip to main content

ci2/
lib.rs

1// Copyright (C) The Strand-Braid Authors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use machine_vision_formats as formats;
5pub use strand_cam_types::{AcquisitionMode, AutoMode, TriggerMode, TriggerSelector};
6use strand_dynamic_frame::DynamicFrameOwned;
7
8// TODO add binning support
9
10// ---------------------------
11// errors
12
13pub type Result<M> = std::result::Result<M, Error>;
14
15#[derive(thiserror::Error, Debug)]
16pub enum Error {
17    #[error("SingleFrameError({0})")]
18    SingleFrameError(String),
19    #[error("Timeout")]
20    Timeout,
21    #[error("CI2Error({msg})")]
22    CI2Error { msg: String },
23    #[error("feature not present")]
24    FeatureNotPresent(),
25    #[error("BackendError({0})")]
26    BackendError(#[from] anyhow::Error),
27    #[error("io error: {source}")]
28    IoError {
29        #[from]
30        source: std::io::Error,
31    },
32    #[error("utf8 error: {source}")]
33    Utf8Error {
34        #[from]
35        source: std::str::Utf8Error,
36    },
37    #[error("try from int error: {source}")]
38    TryFromIntError {
39        #[from]
40        source: std::num::TryFromIntError,
41    },
42}
43
44fn _test_error_is_send() {
45    // Compile-time test to ensure Error implements Send trait.
46    fn implements<T: Send>() {}
47    implements::<Error>();
48}
49
50impl<'a> From<&'a str> for Error {
51    fn from(orig: &'a str) -> Error {
52        Error::CI2Error {
53            msg: orig.to_string(),
54        }
55    }
56}
57
58impl From<String> for Error {
59    fn from(msg: String) -> Error {
60        Error::CI2Error { msg }
61    }
62}
63
64// ---------------------------
65// CameraModule
66
67/// A module for opening cameras (e.g. pylon).
68pub trait CameraModule: Send {
69    type CameraType: Camera;
70    type Guard;
71
72    // TODO: have full_name and friendly_name?
73    fn name(&self) -> &str;
74    fn camera_infos(&self) -> Result<Vec<Box<dyn CameraInfo>>>;
75    fn camera(&mut self, name: &str) -> Result<Self::CameraType>;
76
77    /// The file extension for node map settings.
78    ///
79    /// The strings used in [Camera::node_map_load] and [Camera::node_map_save]
80    /// would typically be stored in files with this extension.
81    fn settings_file_extension(&self) -> &str;
82}
83
84#[derive(Clone)]
85pub struct DynamicFrameWithInfo {
86    /// The image frame acquired from the camera.
87    pub image: std::sync::Arc<DynamicFrameOwned>,
88    /// Frame timing information acquired by the host.
89    pub host_timing: HostTimingInfo,
90    /// Backend-specific information about the frame.
91    ///
92    /// This may contain camera backend-specific timing information, which is
93    /// presumably better than that available using host-only information.
94    /// However, this is not guaranteed to be present.
95    pub backend_data: Option<Box<dyn BackendData>>,
96}
97
98pub trait BackendData: dyn_clone::DynClone + Send + AsAny {}
99
100// see https://users.rust-lang.org/t/calling-any-downcast-ref-requires-static/52071
101pub trait AsAny {
102    fn as_any(&self) -> &dyn std::any::Any;
103}
104impl<T: std::any::Any> AsAny for T {
105    fn as_any(&self) -> &dyn std::any::Any {
106        self
107    }
108}
109
110// implement Clone for BackendData
111dyn_clone::clone_trait_object!(BackendData);
112
113impl DynamicFrameWithInfo {
114    pub fn width(&self) -> u32 {
115        self.image.borrow().width()
116    }
117    pub fn height(&self) -> u32 {
118        self.image.borrow().height()
119    }
120    pub fn pixel_format(&self) -> formats::PixFmt {
121        self.image.borrow().pixel_format()
122    }
123}
124
125/// Timing information acquired on the host computer.
126///
127/// This can be considered the "least common denominator" of frame timing
128/// information, as it will always be present but is not necessarily as accurate
129/// as desired.
130#[derive(Debug, Clone)]
131pub struct HostTimingInfo {
132    /// The frame number as counted by the host.
133    ///
134    /// This can deviate from the "real" frame number if the frames were
135    /// dropped, as might happen if the computer was busy with a different task.
136    pub fno: usize,
137    /// The timestamp of the frame when it was acquired by the host.
138    ///
139    /// This will be at least slightly delayed from the "real" frame timestamp
140    /// by transmission delays. Furthermore, if the computer was busy during
141    /// acquisition there may be additional, highly variable, delays.
142    pub datetime: chrono::DateTime<chrono::Utc>,
143}
144
145// ---------------------------
146// CameraInfo
147
148pub trait CameraInfo {
149    fn name(&self) -> &str;
150    fn serial(&self) -> &str;
151    fn model(&self) -> &str;
152    fn vendor(&self) -> &str;
153}
154
155// ---------------------------
156// Camera
157
158pub trait Camera: CameraInfo + Send {
159    // ----- start: weakly typed but easier to implement API -----
160
161    // fn feature_access_query(&self, name: &str) -> Result<AccessQueryResult>;
162    fn command_execute(&self, name: &str, verify: bool) -> Result<()>;
163    fn feature_bool(&self, name: &str) -> Result<bool>;
164    fn feature_bool_set(&self, name: &str, value: bool) -> Result<()>;
165    fn feature_enum(&self, name: &str) -> Result<String>;
166    fn feature_enum_set(&self, name: &str, value: &str) -> Result<()>;
167    fn feature_float(&self, name: &str) -> Result<f64>;
168    fn feature_float_set(&self, name: &str, value: f64) -> Result<()>;
169    fn feature_int(&self, name: &str) -> Result<i64>;
170    fn feature_int_set(&self, name: &str, value: i64) -> Result<()>;
171
172    // ----- end: weakly typed but easier to implement API -----
173
174    /// Load camera settings from an implementation-dependent settings string.
175    ///
176    /// This would typically be read from a file with extension given by
177    /// [CameraModule::settings_file_extension].
178    fn node_map_load(&self, settings: &str) -> Result<()>;
179    /// Read camera settings to an implementation-dependent settings string.
180    ///
181    /// This would typically be saved to a file with extension given by
182    /// [CameraModule::settings_file_extension].
183    fn node_map_save(&self) -> Result<String>;
184
185    /// Return the sensor width in pixels
186    fn width(&self) -> Result<u32>;
187    /// Return the sensor height in pixels
188    fn height(&self) -> Result<u32>;
189
190    // TODO: add this
191    // fn stride(&self) -> Result<u32>;
192
193    // Settings: PixFmt ----------------------------
194    fn pixel_format(&self) -> Result<formats::PixFmt>;
195    fn possible_pixel_formats(&self) -> Result<Vec<formats::PixFmt>>;
196    fn set_pixel_format(&mut self, pixel_format: formats::PixFmt) -> Result<()>;
197
198    // Settings: Exposure Time ----------------------------
199    /// value given in microseconds
200    fn exposure_time(&self) -> Result<f64>;
201    /// value given in microseconds
202    fn exposure_time_range(&self) -> Result<(f64, f64)>;
203    /// value given in microseconds
204    fn set_exposure_time(&mut self, _: f64) -> Result<()>;
205
206    // Settings: Exposure Time Auto Mode ----------------------------
207    fn exposure_auto(&self) -> Result<AutoMode>;
208    fn set_exposure_auto(&mut self, _: AutoMode) -> Result<()>;
209
210    // Settings: Gain ----------------------------
211    /// value given in dB
212    fn gain(&self) -> Result<f64>;
213    /// value given in dB
214    fn gain_range(&self) -> Result<(f64, f64)>;
215    /// value given in dB
216    fn set_gain(&mut self, _: f64) -> Result<()>;
217
218    // Settings: Gain Auto Mode ----------------------------
219    fn gain_auto(&self) -> Result<AutoMode>;
220    fn set_gain_auto(&mut self, _: AutoMode) -> Result<()>;
221
222    // Settings: TriggerMode ----------------------------
223    fn trigger_mode(&self) -> Result<TriggerMode>;
224    fn set_trigger_mode(&mut self, _: TriggerMode) -> Result<()>;
225
226    // Settings: AcquisitionFrameRateEnable ----------------------------
227    fn acquisition_frame_rate_enable(&self) -> Result<bool>;
228    fn set_acquisition_frame_rate_enable(&mut self, value: bool) -> Result<()>;
229
230    // Settings: AcquisitionFrameRate ----------------------------
231    fn acquisition_frame_rate(&self) -> Result<f64>;
232    fn acquisition_frame_rate_range(&self) -> Result<(f64, f64)>;
233    fn set_acquisition_frame_rate(&mut self, value: f64) -> Result<()>;
234
235    // Settings: TriggerSelector ----------------------------
236    fn trigger_selector(&self) -> Result<TriggerSelector>;
237    fn set_trigger_selector(&mut self, _: TriggerSelector) -> Result<()>;
238
239    // Settings: AcquisitionMode ----------------------------
240    fn acquisition_mode(&self) -> Result<AcquisitionMode>;
241    fn set_acquisition_mode(&mut self, _: AcquisitionMode) -> Result<()>;
242
243    // Set external triggering ------------------------------
244    /// Set the camera to use external triggering using default parameters.
245    ///
246    /// The default parameters may vary by camera backend will ideally use
247    /// a hardware trigger to trigger the start of each frame.
248    fn start_default_external_triggering(&mut self) -> Result<()> {
249        // This is the generic default implementation which may be overriden by
250        // implementors.
251
252        // The trigger selector must be set before the trigger mode.
253        self.set_trigger_selector(TriggerSelector::FrameStart)?;
254        self.set_trigger_mode(TriggerMode::On)
255    }
256
257    fn set_software_frame_rate_limit(&mut self, fps_limit: f64) -> Result<()> {
258        // This is the generic default implementation which may be overriden by
259        // implementors.
260        self.set_acquisition_frame_rate_enable(true)?;
261        self.set_acquisition_frame_rate(fps_limit)
262    }
263
264    // Acquisition ----------------------------
265    fn acquisition_start(&mut self) -> Result<()>;
266    fn acquisition_stop(&mut self) -> Result<()>;
267
268    /// synchronous (blocking) frame acquisition
269    // TODO: enable the ability to enqueue memory locations for new frame data.
270    // This way pre-allocated can be stored to by the library and copies of the
271    // data do not have to be made.
272    // TODO: specify timeout
273    fn next_frame(&mut self) -> Result<DynamicFrameWithInfo>;
274}