1use crate::{
2 dependencies::{load_dependencies, LoadedDependencies},
3 get_version_string, parse_enum_result,
4 trampoline::split_closure,
5 EnumerationResult, PicoDriver,
6};
7use parking_lot::RwLock;
8use pico_common::{
9 ChannelConfig, DownsampleMode, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo,
10 PicoRange, PicoResult, PicoStatus, SampleConfig, ToPicoStr,
11};
12use pico_sys_dynamic::ps2000a::PS2000ALoader;
13use std::{pin::Pin, sync::Arc};
14
15pub struct PS2000ADriver {
16 _dependencies: LoadedDependencies,
17 bindings: PS2000ALoader,
18}
19
20impl std::fmt::Debug for PS2000ADriver {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 f.debug_struct("PS2000ADriver").finish()
23 }
24}
25
26impl PS2000ADriver {
27 pub fn new<P>(path: P) -> Result<Self, ::libloading::Error>
28 where
29 P: AsRef<::std::ffi::OsStr>,
30 {
31 let dependencies = load_dependencies(&path.as_ref());
32 let bindings = unsafe { PS2000ALoader::new(path)? };
33 unsafe { bindings.ps2000aApplyFix(0x1ced9168, 0x11e6) };
35 Ok(PS2000ADriver {
36 bindings,
37 _dependencies: dependencies,
38 })
39 }
40}
41
42impl PicoDriver for PS2000ADriver {
43 fn get_driver(&self) -> Driver {
44 Driver::PS2000A
45 }
46
47 #[tracing::instrument(level = "trace", skip(self))]
48 fn get_version(&self) -> PicoResult<String> {
49 let raw_version = self.get_unit_info(0, PicoInfo::DRIVER_VERSION)?;
50
51 Ok(get_version_string(&raw_version))
54 }
55
56 #[tracing::instrument(level = "trace", skip(self))]
57 fn get_path(&self) -> PicoResult<Option<String>> {
58 Ok(Some(self.get_unit_info(0, PicoInfo::DRIVER_PATH)?))
59 }
60
61 #[tracing::instrument(level = "trace", skip(self))]
62 fn enumerate_units(&self) -> PicoResult<Vec<EnumerationResult>> {
63 let mut device_count = 0;
64 let mut serial_buf = "-v".into_pico_i8_string();
65 serial_buf.extend(vec![0i8; 1000]);
66 let mut serial_buf_len = serial_buf.len() as i16;
67
68 let status = PicoStatus::from(unsafe {
69 self.bindings.ps2000aEnumerateUnits(
70 &mut device_count,
71 serial_buf.as_mut_ptr(),
72 &mut serial_buf_len,
73 )
74 });
75
76 match status {
77 PicoStatus::NOT_FOUND => Ok(Vec::new()),
78 PicoStatus::OK => Ok(parse_enum_result(&serial_buf, serial_buf_len as usize)),
79 x => Err(PicoError::from_status(x, "enumerate_units")),
80 }
81 }
82
83 #[tracing::instrument(level = "trace", skip(self))]
84 fn open_unit(&self, serial: Option<&str>) -> PicoResult<i16> {
85 let serial = serial.map(|s| s.into_pico_i8_string());
86
87 let mut handle = -1i16;
88 let status = PicoStatus::from(unsafe {
89 match serial {
90 Some(mut serial) => self
91 .bindings
92 .ps2000aOpenUnit(&mut handle, serial.as_mut_ptr()),
93 None => self
94 .bindings
95 .ps2000aOpenUnit(&mut handle, std::ptr::null_mut()),
96 }
97 });
98
99 match status {
100 PicoStatus::OK => Ok(handle),
101 x => Err(PicoError::from_status(x, "open_unit")),
102 }
103 }
104
105 #[tracing::instrument(level = "trace", skip(self))]
106 fn ping_unit(&self, handle: i16) -> PicoResult<()> {
107 PicoStatus::from(unsafe { self.bindings.ps2000aPingUnit(handle) })
108 .to_result((), "ping_unit")
109 }
110
111 #[tracing::instrument(level = "trace", skip(self))]
112 fn maximum_value(&self, handle: i16) -> PicoResult<i16> {
113 let mut value = vec![-1i16];
114
115 PicoStatus::from(unsafe {
116 self.bindings
117 .ps2000aMaximumValue(handle, value.as_mut_ptr())
118 })
119 .to_result(value[0], "maximum_value")
120 }
121
122 #[tracing::instrument(level = "trace", skip(self))]
123 fn close(&self, handle: i16) -> PicoResult<()> {
124 PicoStatus::from(unsafe { self.bindings.ps2000aCloseUnit(handle) })
125 .to_result((), "close_unit")
126 }
127
128 #[tracing::instrument(level = "trace", skip(self))]
129 fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String> {
130 let mut string_buf: Vec<i8> = vec![0i8; 256];
131 let mut string_buf_out_len = vec![0i16];
132
133 let status = PicoStatus::from(unsafe {
134 self.bindings.ps2000aGetUnitInfo(
135 handle,
136 string_buf.as_mut_ptr(),
137 string_buf.len() as i16,
138 string_buf_out_len.as_mut_ptr(),
139 info_type.into(),
140 )
141 });
142
143 match status {
144 PicoStatus::OK => Ok(string_buf.from_pico_i8_string(string_buf_out_len[0] as usize)),
145 x => Err(PicoError::from_status(x, "get_unit_info")),
146 }
147 }
148
149 #[tracing::instrument(level = "trace", skip(self))]
150 fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>> {
151 let mut ranges = vec![0i32; 30];
152 let mut len = 30i32;
153
154 let status = PicoStatus::from(unsafe {
155 self.bindings.ps2000aGetChannelInformation(
156 handle,
157 0,
158 0,
159 ranges.as_mut_ptr(),
160 &mut len,
161 channel.into(),
162 )
163 });
164
165 match status {
166 PicoStatus::OK => Ok(ranges[0..len as usize]
167 .to_vec()
168 .iter()
169 .map(|v| PicoRange::from(*v))
170 .collect()),
171 x => Err(PicoError::from_status(x, "get_channel_ranges")),
172 }
173 }
174
175 #[tracing::instrument(level = "trace", skip(self))]
176 fn enable_channel(
177 &self,
178 handle: i16,
179 channel: PicoChannel,
180 config: &ChannelConfig,
181 ) -> PicoResult<()> {
182 PicoStatus::from(unsafe {
183 self.bindings.ps2000aSetChannel(
184 handle,
185 channel.into(),
186 1,
187 config.coupling.into(),
188 config.range.into(),
189 config.offset as f32,
190 )
191 })
192 .to_result((), "set_channel")
193 }
194
195 #[tracing::instrument(level = "trace", skip(self))]
196 fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()> {
197 PicoStatus::from(unsafe {
198 self.bindings
199 .ps2000aSetChannel(handle, channel.into(), 0, 0, 0, 0.0)
200 })
201 .to_result((), "set_channel")
202 }
203
204 #[tracing::instrument(level = "trace", skip(self, buffer))]
205 fn set_data_buffer(
206 &self,
207 handle: i16,
208 channel: PicoChannel,
209 buffer: Arc<RwLock<Pin<Vec<i16>>>>,
210 buffer_len: usize,
211 ) -> PicoResult<()> {
212 let mut buffer = buffer.write();
213
214 PicoStatus::from(unsafe {
215 self.bindings.ps2000aSetDataBuffer(
216 handle,
217 channel.into(),
218 buffer.as_mut_ptr(),
219 buffer_len as i32,
220 0,
221 DownsampleMode::NONE.into(),
222 )
223 })
224 .to_result((), "set_data_buffer")
225 }
226
227 #[tracing::instrument(level = "trace", skip(self))]
228 fn start_streaming(
229 &self,
230 handle: i16,
231 sample_config: &SampleConfig,
232 ) -> PicoResult<SampleConfig> {
233 let mut sample_interval = sample_config.interval;
234
235 PicoStatus::from(unsafe {
236 self.bindings.ps2000aRunStreaming(
237 handle,
238 &mut sample_interval,
239 sample_config.units.into(),
240 0,
241 0,
242 (false).into(),
243 1,
244 DownsampleMode::NONE.into(),
245 sample_config.samples_per_second(),
246 )
247 })
248 .to_result(
249 sample_config.with_interval(sample_interval),
250 "start_streaming",
251 )
252 }
253
254 #[tracing::instrument(level = "trace", skip(self, callback))]
255 fn get_latest_streaming_values<'a>(
256 &self,
257 handle: i16,
258 _channels: &[PicoChannel],
259 mut callback: Box<dyn FnMut(usize, usize) + 'a>,
260 ) -> PicoResult<()> {
261 let mut full_closure =
262 |_: i16, no_of_samples: i32, start_index: u32, _: i16, _: u32, _: i16, _: i16| {
263 callback(start_index as usize, no_of_samples as usize);
264 };
265
266 let status = PicoStatus::from(unsafe {
267 let (state, callback) = split_closure(&mut full_closure);
268
269 self.bindings
270 .ps2000aGetStreamingLatestValues(handle, Some(callback), state)
271 });
272
273 match status {
274 PicoStatus::OK | PicoStatus::BUSY => Ok(()),
275 x => Err(PicoError::from_status(x, "get_latest_streaming_values")),
276 }
277 }
278
279 #[tracing::instrument(level = "trace", skip(self))]
280 fn stop(&self, handle: i16) -> PicoResult<()> {
281 PicoStatus::from(unsafe { self.bindings.ps2000aStop(handle) }).to_result((), "stop")
282 }
283}