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