1#![allow(clippy::upper_case_acronyms)]
45
46use parking_lot::RwLock;
47use pico_common::{
48 ChannelConfig, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo, PicoRange, PicoResult,
49 SampleConfig,
50};
51pub use resolution::Resolution;
52use std::{fmt, pin::Pin, sync::Arc};
53use thiserror::Error;
54use version_compare::Version;
55
56mod dependencies;
57pub mod kernel_driver;
58pub mod ps2000;
59pub mod ps2000a;
60pub mod ps3000a;
61pub mod ps4000;
62pub mod ps4000a;
63pub mod ps5000a;
64pub mod ps6000;
65pub mod ps6000a;
66mod resolution;
67mod trampoline;
68
69#[derive(Error, Debug)]
71pub enum DriverLoadError {
72 #[error("Pico driver error: {0}")]
73 DriverError(#[from] PicoError),
74
75 #[error("Library load error: {0}")]
76 LibloadingError(#[from] libloading::Error),
77
78 #[error("Invalid Driver Version: Requires >= {required}, Found: {found}")]
79 VersionError { found: String, required: String },
80}
81
82#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
84#[derive(Clone, Debug)]
85pub struct EnumerationResult {
86 pub variant: String,
87 pub serial: String,
88}
89
90pub trait PicoDriver: fmt::Debug + Send + Sync {
92 fn get_driver(&self) -> Driver;
94 fn get_version(&self) -> PicoResult<String>;
96 fn get_path(&self) -> PicoResult<Option<String>>;
98 fn enumerate_units(&self) -> PicoResult<Vec<EnumerationResult>>;
100 fn open_unit(&self, serial: Option<&str>) -> PicoResult<i16>;
102 fn ping_unit(&self, handle: i16) -> PicoResult<()>;
104 fn maximum_value(&self, handle: i16) -> PicoResult<i16>;
106 fn close(&self, handle: i16) -> PicoResult<()>;
108 fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String>;
110 fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>>;
112 fn enable_channel(
114 &self,
115 handle: i16,
116 channel: PicoChannel,
117 config: &ChannelConfig,
118 ) -> PicoResult<()>;
119 fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()>;
121 fn set_data_buffer(
123 &self,
124 handle: i16,
125 channel: PicoChannel,
126 buffer: Arc<RwLock<Pin<Vec<i16>>>>,
127 buffer_len: usize,
128 ) -> PicoResult<()>;
129 fn start_streaming(
131 &self,
132 handle: i16,
133 sample_config: &SampleConfig,
134 ) -> PicoResult<SampleConfig>;
135 fn get_latest_streaming_values<'a>(
137 &self,
138 handle: i16,
139 channels: &[PicoChannel],
140 callback: Box<dyn FnMut(usize, usize) + 'a>,
141 ) -> PicoResult<()>;
142 fn stop(&self, handle: i16) -> PicoResult<()>;
144 fn check_version(&self) -> Result<(), DriverLoadError> {
146 let loaded_str = &self.get_version()?;
147 let loaded_version = Version::from(loaded_str);
148
149 #[allow(clippy::expect_fun_call)]
150 let required_str = get_min_required_version(self.get_driver());
151 let required_version = Version::from(required_str);
152
153 if loaded_version < required_version {
154 Err(DriverLoadError::VersionError {
155 found: loaded_str.to_string(),
156 required: required_str.to_string(),
157 })
158 } else {
159 Ok(())
160 }
161 }
162}
163
164pub type ArcDriver = Arc<dyn PicoDriver>;
165
166pub(crate) fn get_version_string(input: &str) -> String {
167 input
168 .split(|s| s == ' ' || s == ',')
169 .last()
170 .expect("Invalid version string")
171 .to_string()
172}
173
174pub(crate) fn parse_enum_result(buffer: &[i8], len: usize) -> Vec<EnumerationResult> {
175 let serials_list = buffer.from_pico_i8_string(len);
176
177 serials_list
178 .split(',')
179 .map(String::from)
180 .map(|device| {
181 let parts = device.split('[').collect::<Vec<_>>();
182
183 EnumerationResult {
184 serial: parts[0].to_string(),
185 variant: parts[1].trim_end_matches(']').to_string(),
186 }
187 })
188 .collect()
189}
190
191fn get_min_required_version(driver: Driver) -> &'static str {
193 match driver {
194 Driver::PS2000 => "3.0.30.1878",
195 Driver::PS2000A
196 | Driver::PS3000A
197 | Driver::PS4000
198 | Driver::PS4000A
199 | Driver::PS5000A
200 | Driver::PS6000 => "2.1.30.1878",
201 Driver::PS6000A => "1.0.54.2438",
202 _ => panic!(
203 "We don't know the minimum required version for the {:?} driver!",
204 driver,
205 ),
206 }
207}
208
209pub trait LoadDriverExt {
211 fn try_load(&self) -> Result<ArcDriver, DriverLoadError>;
212 fn try_load_with_resolution(
213 &self,
214 resolution: &Resolution,
215 ) -> Result<ArcDriver, DriverLoadError>;
216}
217
218impl LoadDriverExt for Driver {
219 fn try_load(&self) -> Result<ArcDriver, DriverLoadError> {
220 self.try_load_with_resolution(&Default::default())
221 }
222
223 fn try_load_with_resolution(
224 &self,
225 resolution: &Resolution,
226 ) -> Result<ArcDriver, DriverLoadError> {
227 let path = resolution.get_path(*self);
228 Ok(match self {
229 Driver::PS2000 => Arc::new(ps2000::PS2000Driver::new(path)?),
230 Driver::PS2000A => Arc::new(ps2000a::PS2000ADriver::new(path)?),
231 Driver::PS3000A => Arc::new(ps3000a::PS3000ADriver::new(path)?),
232 Driver::PS4000 => Arc::new(ps4000::PS4000Driver::new(path)?),
233 Driver::PS4000A => Arc::new(ps4000a::PS4000ADriver::new(path)?),
234 Driver::PS5000A => Arc::new(ps5000a::PS5000ADriver::new(path)?),
235 Driver::PS6000 => Arc::new(ps6000::PS6000Driver::new(path)?),
236 Driver::PS6000A => Arc::new(ps6000a::PS6000ADriver::new(path)?),
237 Driver::PicoIPP | Driver::IOMP5 => {
238 panic!("These are libraries used by Pico drivers and cannot be loaded directly")
239 }
240 })
241 }
242}