pico_device/
lib.rs

1#![forbid(unsafe_code)]
2
3//! `PicoDevice` implementation for Pico Technology oscilloscope drivers.
4//!
5//! This is a sub crate that you probably don't want to use directly. Try the top level
6//! [`pico-sdk`](https://crates.io/crates/pico-sdk) crate which exposes everything from here.
7//!
8//! When a `PicoDevice` is created, it is opened, its channels and capabilities are
9//! automatically detected and then it is closed.
10//!
11//! # Example
12//! ```no_run
13//! # fn run() -> Result<(),Box<dyn std::error::Error>> {
14//! use pico_common::Driver;
15//! use pico_driver::LoadDriverExt;
16//! use pico_device::PicoDevice;
17//!
18//! // Load the required driver
19//! let driver = Driver::PS2000.try_load()?;
20//!
21//! // Try and open the first available ps2000 device
22//! let device1 = PicoDevice::try_open(&driver, None)?;
23//!
24//! // Try and open devices by serial
25//! let device2 = PicoDevice::try_open(&driver, Some("ABC/123"))?;
26//! let device3 = PicoDevice::try_open(&driver, Some("ABC/987"))?;
27//! # Ok(())
28//! # }
29//! ```
30
31use parking_lot::Mutex;
32use pico_common::{PicoChannel, PicoInfo, PicoRange, PicoResult};
33use pico_driver::{ArcDriver, PicoDriver};
34use std::{
35    collections::HashMap,
36    fmt,
37    fmt::{Debug, Display},
38    sync::Arc,
39};
40
41pub type HandleMutex = Arc<Mutex<Option<i16>>>;
42
43/// Base Pico device
44#[cfg_attr(feature = "serde", derive(serde::Serialize))]
45#[derive(Clone)]
46pub struct PicoDevice {
47    #[cfg_attr(feature = "serde", serde(skip))]
48    pub handle: HandleMutex,
49    #[cfg_attr(feature = "serde", serde(skip))]
50    pub driver: ArcDriver,
51    pub variant: String,
52    pub serial: String,
53    pub usb_version: String,
54    #[cfg_attr(feature = "serde", serde(skip))]
55    pub max_adc_value: i16,
56    pub channel_ranges: HashMap<PicoChannel, Vec<PicoRange>>,
57}
58
59impl Debug for PicoDevice {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
61        f.debug_struct("PicoDevice")
62            .field("variant", &self.variant)
63            .field("serial", &self.serial)
64            .finish()
65    }
66}
67
68impl Display for PicoDevice {
69    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70        write!(f, "{} ({})", self.variant, self.serial)
71    }
72}
73
74impl PicoDevice {
75    /// Creates a PicoDevice with the supplied `PicoDriver` and serial string.
76    /// If `None` is passed for the serial, the first discovered device will be
77    /// opened.
78    /// ```no_run
79    /// use pico_common::Driver;
80    /// use pico_driver::LoadDriverExt;
81    /// use pico_device::PicoDevice;
82    ///
83    /// // Load the required driver with a specific resolution
84    /// let driver = Driver::PS2000.try_load().unwrap();
85    /// let device1 = PicoDevice::try_open(&driver, Some("ABC/123")).unwrap();
86    /// let device2 = PicoDevice::try_open(&driver, Some("ABC/987")).unwrap();
87    ///
88    /// assert_eq!(device1.variant, "2204A");
89    /// assert_eq!(device2.variant, "2205A");
90    /// ```
91    pub fn try_open(driver: &Arc<dyn PicoDriver>, serial: Option<&str>) -> PicoResult<PicoDevice> {
92        let handle = driver.open_unit(serial)?;
93
94        let serial = match serial {
95            Some(s) => s.to_string(),
96            None => driver.get_unit_info(handle, PicoInfo::BATCH_AND_SERIAL)?,
97        };
98
99        let variant = driver.get_unit_info(handle, PicoInfo::VARIANT_INFO)?;
100        let usb_version = driver.get_unit_info(handle, PicoInfo::USB_VERSION)?;
101
102        // Get the second letter of the device variant to get the number of channels
103        let ch_count = variant[1..2]
104            .parse::<i32>()
105            .expect("Could not parse device variant for number of channels");
106
107        let channel_ranges = (0..ch_count)
108            .map(|ch| -> PicoResult<(PicoChannel, Vec<_>)> {
109                let ch: PicoChannel = ch.into();
110                Ok((ch, driver.get_channel_ranges(handle, ch)?))
111            })
112            // Some channels will error if they are disabled due to power limitations
113            .flatten()
114            .collect();
115
116        let max_adc_value = driver.maximum_value(handle)?;
117
118        Ok(PicoDevice {
119            handle: Arc::new(Mutex::new(Some(handle))),
120            driver: driver.clone(),
121            serial,
122            variant,
123            usb_version,
124            max_adc_value,
125            channel_ranges,
126        })
127    }
128
129    pub fn get_channels(&self) -> Vec<PicoChannel> {
130        self.channel_ranges.keys().copied().collect()
131    }
132}
133
134impl Drop for PicoDevice {
135    #[tracing::instrument(level = "trace", skip(self))]
136    fn drop(&mut self) {
137        if let Some(handle) = self.handle.lock().take() {
138            let _ = self.driver.close(handle);
139        }
140    }
141}