1use crate::{
2 dependencies::{load_dependencies, LoadedDependencies},
3 get_version_string, EnumerationResult, PicoDriver,
4};
5use lazy_static::lazy_static;
6use parking_lot::{Mutex, RwLock};
7use pico_common::{
8 ChannelConfig, Driver, FromPicoStr, PicoChannel, PicoCoupling, PicoError, PicoInfo, PicoRange,
9 PicoResult, PicoStatus, SampleConfig,
10};
11use pico_sys_dynamic::ps2000::PS2000Loader;
12use std::{collections::HashMap, pin::Pin, sync::Arc};
13
14type BufferMap = HashMap<PicoChannel, Arc<RwLock<Pin<Vec<i16>>>>>;
15
16lazy_static! {
17 static ref BUFFERS: Mutex<HashMap<i16, BufferMap>> = Default::default();
19}
20
21struct CallbackRef {
22 handle: i16,
23 index: usize,
24}
25
26#[derive(Default)]
27struct LockedCallbackRef {
28 inner: Mutex<Option<CallbackRef>>,
29}
30
31impl LockedCallbackRef {
32 fn start(&self, handle: i16) {
33 loop {
34 let mut inner = self.inner.lock();
35
36 if inner.is_none() {
39 *inner = Some(CallbackRef { handle, index: 0 });
40 return;
41 } else {
42 std::thread::yield_now();
43 }
44 }
45 }
46
47 fn callback(&self, overview_buffers: *const *const usize, n_values: usize) {
48 let mut inner = self.inner.lock();
49
50 if let Some(mut callback_ref) = inner.take() {
51 let buffer_pointers =
52 unsafe { std::slice::from_raw_parts::<*const usize>(overview_buffers, 4) };
53
54 let mut all_buffers = BUFFERS.lock();
55 let buffers = all_buffers
56 .get_mut(&callback_ref.handle)
57 .expect("Could not find buffers for this device");
58
59 let mut copy_data = |index: usize, ch: PicoChannel| {
60 let raw_data = unsafe {
61 std::slice::from_raw_parts::<i16>(
62 buffer_pointers[index] as *const i16,
63 n_values,
64 )
65 };
66 let mut ch_buf = buffers
68 .get_mut(&ch)
69 .expect("Could not find buffers for this channel")
70 .write();
71
72 ch_buf[callback_ref.index..callback_ref.index + n_values]
73 .copy_from_slice(&raw_data);
74 };
75
76 if !buffer_pointers[0].is_null() {
78 copy_data(0, PicoChannel::A)
79 }
80
81 if !buffer_pointers[2].is_null() {
82 copy_data(2, PicoChannel::B)
83 }
84
85 callback_ref.index += n_values as usize;
86 *inner = Some(callback_ref);
87 } else {
88 panic!("Streaming callback was called without a device reference");
89 }
90 }
91
92 fn end(&self) -> Option<usize> {
93 let mut inner = self.inner.lock();
94 inner.take().map(|cb| cb.index)
95 }
96}
97
98lazy_static! {
99 static ref CALLBACK_REF: LockedCallbackRef = Default::default();
107}
108
109extern "C" fn streaming_callback(
110 overview_buffers: *mut *mut i16,
111 _overflow: i16,
112 _triggered_at: u32,
113 _triggered: i16,
114 _auto_stop: i16,
115 n_values: u32,
116) {
117 CALLBACK_REF.callback(overview_buffers as *const *const usize, n_values as usize);
118}
119
120pub struct PS2000Driver {
121 _dependencies: LoadedDependencies,
122 bindings: PS2000Loader,
123}
124
125impl std::fmt::Debug for PS2000Driver {
126 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127 f.debug_struct("PS2000Driver").finish()
128 }
129}
130
131impl PS2000Driver {
132 pub fn new<P>(path: P) -> Result<Self, ::libloading::Error>
133 where
134 P: AsRef<::std::ffi::OsStr>,
135 {
136 let dependencies = load_dependencies(&path.as_ref());
137 let bindings = unsafe { PS2000Loader::new(path)? };
138 unsafe { bindings.ps2000_apply_fix(0x1ced9168, 0x11e6) };
139 Ok(PS2000Driver {
140 bindings,
141 _dependencies: dependencies,
142 })
143 }
144
145 fn open_unit_base(&self) -> Result<i16, PicoStatus> {
146 match unsafe { self.bindings.ps2000_open_unit() } {
147 -1 => Err(PicoStatus::OPERATION_FAILED),
148 0 => Err(PicoStatus::NOT_FOUND),
149 handle => Ok(handle),
150 }
151 }
152}
153
154impl PicoDriver for PS2000Driver {
155 fn get_driver(&self) -> Driver {
156 Driver::PS2000
157 }
158
159 #[tracing::instrument(level = "trace", skip(self))]
160 fn get_version(&self) -> PicoResult<String> {
161 let raw_version = self.get_unit_info(0, PicoInfo::DRIVER_VERSION)?;
162
163 Ok(get_version_string(&raw_version))
166 }
167
168 #[tracing::instrument(level = "trace", skip(self))]
169 fn get_path(&self) -> PicoResult<Option<String>> {
170 Ok(None)
171 }
172
173 #[tracing::instrument(level = "trace", skip(self))]
177 fn enumerate_units(&self) -> PicoResult<Vec<EnumerationResult>> {
178 let mut output = Vec::new();
179 let mut handles_to_close = Vec::new();
181
182 loop {
183 match self.open_unit_base() {
184 Ok(handle) => {
185 handles_to_close.push(handle);
186
187 let serial = self.get_unit_info(handle, PicoInfo::BATCH_AND_SERIAL)?;
188 let variant = self.get_unit_info(handle, PicoInfo::VARIANT_INFO)?;
189 output.push(EnumerationResult { variant, serial });
190 }
191 Err(PicoStatus::NOT_FOUND) => break,
192 Err(e) => {
193 for each in handles_to_close {
194 let _ = self.close(each);
195 }
196
197 return Err(PicoError::from_status(e, "open_unit"));
198 }
199 }
200 }
201
202 for each in handles_to_close {
203 let _ = self.close(each);
204 }
205
206 Ok(output)
207 }
208
209 #[tracing::instrument(level = "trace", skip(self))]
213 fn open_unit(&self, serial: Option<&str>) -> PicoResult<i16> {
214 let mut handles_to_close = Vec::new();
216
217 loop {
218 match self.open_unit_base() {
219 Ok(handle) => {
220 if let Some(serial) = serial {
221 if serial == self.get_unit_info(handle, PicoInfo::BATCH_AND_SERIAL)? {
222 for each in handles_to_close {
223 let _ = self.close(each);
224 }
225
226 return Ok(handle);
227 } else {
228 handles_to_close.push(handle);
229 }
230 } else {
231 return Ok(handle);
232 }
233 }
234 Err(e) => {
235 for each in handles_to_close {
236 let _ = self.close(each);
237 }
238
239 return Err(PicoError::from_status(e, "open_unit"));
240 }
241 }
242 }
243 }
244
245 #[tracing::instrument(level = "trace", skip(self))]
246 fn ping_unit(&self, handle: i16) -> PicoResult<()> {
247 PicoStatus::from(unsafe { self.bindings.ps2000PingUnit(handle) }).to_result((), "ping_unit")
248 }
249
250 #[tracing::instrument(level = "trace", skip(self))]
251 fn maximum_value(&self, _: i16) -> PicoResult<i16> {
252 Ok(32_767)
254 }
255
256 #[tracing::instrument(level = "trace", skip(self))]
257 fn close(&self, handle: i16) -> PicoResult<()> {
258 let mut buffers = BUFFERS.lock();
260 buffers.remove(&handle);
261
262 PicoStatus::from(unsafe { self.bindings.ps2000_close_unit(handle) })
263 .to_result((), "close_unit")
264 }
265
266 #[tracing::instrument(level = "trace", skip(self))]
267 fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String> {
268 let mut string_buf: Vec<i8> = vec![0i8; 256];
269
270 let status = PicoStatus::from(unsafe {
271 self.bindings.ps2000_get_unit_info(
272 handle,
273 string_buf.as_mut_ptr(),
274 string_buf.len() as i16,
275 info_type.into(),
276 )
277 });
278
279 match status {
280 PicoStatus::OK => Ok(string_buf.from_pico_i8_string(255)),
281 x => Err(PicoError::from_status(x, "get_unit_info")),
282 }
283 }
284
285 #[tracing::instrument(level = "trace", skip(self))]
286 fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>> {
287 Ok((1..=10)
291 .map(|r| -> PicoResult<PicoRange> {
292 let range = PicoRange::from(r);
293 let config = ChannelConfig {
294 coupling: PicoCoupling::DC,
295 range,
296 offset: 0.0,
297 };
298
299 self.enable_channel(handle, channel, &config)?;
300 Ok(range)
301 })
302 .flatten()
303 .collect())
304 }
305
306 #[tracing::instrument(level = "trace", skip(self))]
307 fn enable_channel(
308 &self,
309 handle: i16,
310 channel: PicoChannel,
311 config: &ChannelConfig,
312 ) -> PicoResult<()> {
313 PicoStatus::from(unsafe {
314 self.bindings.ps2000_set_channel(
315 handle,
316 channel.into(),
317 1,
318 config.coupling.into(),
319 config.range.into(),
320 )
321 })
322 .to_result((), "set_channel")
323 }
324
325 fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()> {
326 PicoStatus::from(unsafe {
327 self.bindings
328 .ps2000_set_channel(handle, channel.into(), 0, 0, 0)
329 })
330 .to_result((), "set_channel")
331 }
332
333 #[tracing::instrument(level = "trace", skip(self, buffer))]
337 fn set_data_buffer(
338 &self,
339 handle: i16,
340 channel: PicoChannel,
341 buffer: Arc<RwLock<Pin<Vec<i16>>>>,
342 _buffer_len: usize,
343 ) -> PicoResult<()> {
344 let mut buffers = BUFFERS.lock();
345
346 buffers
347 .entry(handle)
348 .and_modify(|e| {
349 e.insert(channel, buffer.clone());
350 })
351 .or_insert_with(|| {
352 let mut hashmap = HashMap::new();
353 hashmap.insert(channel, buffer);
354 hashmap
355 });
356
357 Ok(())
358 }
359
360 #[tracing::instrument(level = "trace", skip(self))]
361 fn start_streaming(
362 &self,
363 handle: i16,
364 sample_config: &SampleConfig,
365 ) -> PicoResult<SampleConfig> {
366 let status = PicoStatus::from(unsafe {
367 self.bindings.ps2000_run_streaming_ns(
368 handle,
369 sample_config.interval,
370 sample_config.units.into(),
371 sample_config.samples_per_second(),
372 (false).into(),
373 1,
374 1_000_000,
375 )
376 });
377
378 status.to_result(*sample_config, "start_streaming")
384 }
385
386 #[tracing::instrument(level = "trace", skip(self, callback))]
387 fn get_latest_streaming_values<'a>(
388 &self,
389 handle: i16,
390 _channels: &[PicoChannel],
391 mut callback: Box<dyn FnMut(usize, usize) + 'a>,
392 ) -> PicoResult<()> {
393 CALLBACK_REF.start(handle);
394
395 unsafe {
396 self.bindings
397 .ps2000_get_streaming_last_values(handle, Some(streaming_callback))
398 };
399
400 if let Some(sample_count) = CALLBACK_REF.end() {
401 callback(0, sample_count);
402 }
403
404 Ok(())
405 }
406
407 #[tracing::instrument(level = "trace", skip(self))]
408 fn stop(&self, handle: i16) -> PicoResult<()> {
409 PicoStatus::from(unsafe { self.bindings.ps2000_stop(handle) }).to_result((), "stop")
410 }
411}
412
413impl Drop for PS2000Driver {
414 #[tracing::instrument(level = "trace", skip(self))]
415 fn drop(&mut self) {}
416}