realsense_rust/
context.rs

1//! Type that defines a RealSense context used by the rest of the API
2
3use crate::{
4    base::from_path,
5    check_rs2_error,
6    device::Device,
7    device_hub::DeviceHub,
8    kind::{Rs2Exception, Rs2ProductLine},
9};
10use anyhow::Result;
11use num_traits::ToPrimitive;
12use realsense_sys as sys;
13use std::{collections::HashSet, convert::From, path::Path, ptr::NonNull};
14use thiserror::Error;
15
16/// Type describing a RealSense context, used by the rest of the API.
17#[derive(Debug)]
18pub struct Context {
19    /// A non-null pointer to the underlying librealsense context.
20    context_ptr: NonNull<sys::rs2_context>,
21}
22
23/// An error type describing failure to construct a context.
24#[derive(Error, Debug)]
25#[error("Could not construct the context. Type: {0}; Reason: {1}")]
26pub struct ContextConstructionError(pub Rs2Exception, pub String);
27
28/// An error type describing failure to get the device hub from a context.
29#[derive(Error, Debug)]
30#[error("Could not get the device hub from the context. Type: {0}; Reason: {1}")]
31pub struct CouldNotGetDeviceHubError(pub Rs2Exception, pub String);
32
33/// An error type describing failure to add a device from a file.
34#[derive(Error, Debug)]
35#[error("Could not add a device from file. Type: {0}; Reason: {1}")]
36pub struct CouldNotAddDeviceError(pub Rs2Exception, pub String);
37
38/// An error type describing failure to remove a device from a file.
39#[derive(Error, Debug)]
40#[error("Could not remove device from file. Type: {0}; Reason: {1}")]
41pub struct CouldNotRemoveDeviceError(pub Rs2Exception, pub String);
42
43impl Drop for Context {
44    fn drop(&mut self) {
45        unsafe { sys::rs2_delete_context(self.context_ptr.as_ptr()) }
46    }
47}
48
49unsafe impl Send for Context {}
50
51impl Context {
52    /// Construct a new context.
53    ///
54    /// # Errors
55    ///
56    /// Returns [`ContextConstructionError`] if the context cannot be created.
57    ///
58    pub fn new() -> Result<Self, ContextConstructionError> {
59        unsafe {
60            let mut err = std::ptr::null_mut::<sys::rs2_error>();
61            let ptr = sys::rs2_create_context(sys::RS2_API_VERSION as i32, &mut err);
62            check_rs2_error!(err, ContextConstructionError)?;
63
64            Ok(Self {
65                context_ptr: NonNull::new(ptr).unwrap(),
66            })
67        }
68    }
69
70    /// Creates a device hub from the context.
71    ///
72    /// # Errors
73    ///
74    /// Returns [`CouldNotGetDeviceHubError`] if the device hub cannot be created.
75    ///
76    pub fn create_device_hub(&self) -> Result<DeviceHub, CouldNotGetDeviceHubError> {
77        unsafe {
78            let mut err = std::ptr::null_mut::<sys::rs2_error>();
79            let devicehub_ptr = sys::rs2_create_device_hub(self.context_ptr.as_ptr(), &mut err);
80            check_rs2_error!(err, CouldNotGetDeviceHubError)?;
81
82            Ok(DeviceHub::from(NonNull::new(devicehub_ptr).unwrap()))
83        }
84    }
85
86    /// Get a list of devices that are already connected to the host.
87    pub fn query_devices(&self, product_mask: HashSet<Rs2ProductLine>) -> Vec<Device> {
88        // TODO/TEST: Make sure that an empty mask (therefore giving no filter) gives
89        // us _all_ devices, not _no_ devices.
90
91        let mask = if product_mask.is_empty() {
92            Rs2ProductLine::Any.to_i32().unwrap()
93        } else {
94            product_mask.iter().fold(0, |k, v| k | v.to_u32().unwrap()) as i32
95        };
96
97        let mut devices = Vec::new();
98        unsafe {
99            let mut err = std::ptr::null_mut::<sys::rs2_error>();
100            let device_list_ptr =
101                sys::rs2_query_devices_ex(self.context_ptr.as_ptr(), mask, &mut err);
102
103            if err.as_ref().is_some() {
104                sys::rs2_free_error(err);
105                return devices;
106            }
107
108            let device_list = NonNull::new(device_list_ptr).unwrap();
109
110            let len = sys::rs2_get_device_count(device_list.as_ptr(), &mut err);
111
112            if err.as_ref().is_some() {
113                sys::rs2_free_error(err);
114                sys::rs2_delete_device_list(device_list.as_ptr());
115                return devices;
116            }
117
118            for i in 0..len {
119                match Device::try_create(&device_list, i) {
120                    Ok(d) => {
121                        devices.push(d);
122                    }
123                    Err(_) => {
124                        continue;
125                    }
126                }
127            }
128
129            sys::rs2_delete_device_list(device_list.as_ptr());
130        }
131        devices
132    }
133
134    /// Create a new device and add it to the context.
135    ///
136    /// This adds a "device" at a particular file on the system to the RealSense context. Returns a
137    /// handle to the device, or an error if this call fails.
138    ///
139    /// # Errors
140    ///
141    /// Returns [`NulError`](std::ffi::NulError) if the provided file path cannot be cleanly
142    /// represented as a [`CString`](std::ffi::CString). This usually only occurs if you have null
143    /// characters in the path. Constructing a path using the utilties in Rust's [`std::fs`] are
144    /// expected to work.
145    ///
146    /// Returns [`CouldNotAddDeviceError`] if the device cannot be added.
147    ///
148    pub fn add_device<P>(&mut self, file: P) -> Result<Device>
149    where
150        P: AsRef<Path>,
151    {
152        let path = from_path(file)?;
153        unsafe {
154            let mut err = std::ptr::null_mut::<sys::rs2_error>();
155            let device_ptr =
156                sys::rs2_context_add_device(self.context_ptr.as_ptr(), path.as_ptr(), &mut err);
157            check_rs2_error!(err, CouldNotAddDeviceError)?;
158
159            Ok(Device::from(NonNull::new(device_ptr).unwrap()))
160        }
161    }
162
163    /// Removes a playback device from the context, if it exists
164    ///
165    /// This removes a "device" at a particular file on the system from the RealSense context.
166    /// Returns nothing (null tuple) or an Error if the device cannot be removed.
167    ///
168    /// # Errors
169    ///
170    /// Returns [`CouldNotRemoveDeviceError`] if the device cannot be removed for any reason.
171    ///
172    pub fn remove_device<P>(&mut self, file: P) -> Result<()>
173    where
174        P: AsRef<Path>,
175    {
176        let path = from_path(file)?;
177        unsafe {
178            let mut err = std::ptr::null_mut::<sys::rs2_error>();
179            sys::rs2_context_remove_device(self.context_ptr.as_ptr(), path.as_ptr(), &mut err);
180            check_rs2_error!(err, CouldNotRemoveDeviceError)?;
181
182            Ok(())
183        }
184    }
185
186    /// Get the underlying low-level pointer to the context object.
187    ///
188    /// # Safety
189    ///
190    /// This method is not intended to be called or used outside of the crate itself. Be warned, it
191    /// is _undefined behaviour_ to call [`realsense_sys::rs2_delete_context`] on this pointer. If
192    /// you do, you risk a double-free error when the [`Context`] struct itself is dropped.
193    ///
194    pub(crate) unsafe fn get_raw(&self) -> NonNull<sys::rs2_context> {
195        self.context_ptr
196    }
197}