#![allow(clippy::upper_case_acronyms)]
use parking_lot::RwLock;
use pico_common::{
ChannelConfig, Driver, FromPicoStr, PicoChannel, PicoError, PicoInfo, PicoRange, PicoResult,
SampleConfig,
};
pub use resolution::LibraryResolution;
use std::{fmt, sync::Arc};
use thiserror::Error;
use version_compare::Version;
mod dependencies;
pub mod kernel_driver;
pub mod ps2000;
pub mod ps2000a;
pub mod ps3000a;
pub mod ps4000;
pub mod ps4000a;
pub mod ps5000a;
pub mod ps6000;
pub mod ps6000a;
pub mod psospa;
mod resolution;
mod trampoline;
#[derive(Error, Debug)]
pub enum DriverLoadError {
#[error("Pico driver error: {0}")]
DriverError(#[from] PicoError),
#[error("Library load error: {0}")]
LibloadingError(#[from] libloading::Error),
#[error("Invalid Driver Version: Requires >= {required}, Found: {found}")]
VersionError { found: String, required: String },
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug)]
pub struct EnumerationResult {
pub variant: String,
pub serial: String,
}
pub trait PicoDriver: fmt::Debug + Send + Sync {
fn get_driver(&self) -> Driver;
fn get_version(&self) -> PicoResult<String>;
fn get_path(&self) -> PicoResult<Option<String>>;
fn enumerate_units(&self) -> PicoResult<Vec<EnumerationResult>>;
fn open_unit(&self, serial: Option<&str>) -> PicoResult<i16>;
fn ping_unit(&self, handle: i16) -> PicoResult<()>;
fn maximum_value(&self, handle: i16) -> PicoResult<i16>;
fn close(&self, handle: i16) -> PicoResult<()>;
fn get_unit_info(&self, handle: i16, info_type: PicoInfo) -> PicoResult<String>;
fn get_channel_ranges(&self, handle: i16, channel: PicoChannel) -> PicoResult<Vec<PicoRange>>;
fn enable_channel(
&self,
handle: i16,
channel: PicoChannel,
config: &ChannelConfig,
) -> PicoResult<()>;
fn disable_channel(&self, handle: i16, channel: PicoChannel) -> PicoResult<()>;
fn set_data_buffer(
&self,
handle: i16,
channel: PicoChannel,
buffer: Arc<RwLock<Vec<i16>>>,
buffer_len: usize,
) -> PicoResult<()>;
fn start_streaming(
&self,
handle: i16,
sample_config: &SampleConfig,
enabled_channels: u8,
) -> PicoResult<SampleConfig>;
fn get_latest_streaming_values<'a>(
&self,
handle: i16,
channels: &[PicoChannel],
callback: Box<dyn FnMut(usize, usize) + 'a>,
) -> PicoResult<()>;
fn stop(&self, handle: i16) -> PicoResult<()>;
fn check_version(&self) -> Result<(), DriverLoadError> {
let loaded_str = &self.get_version()?;
let loaded_version = Version::from(loaded_str);
let required_str = get_min_required_version(self.get_driver());
let required_version = Version::from(required_str);
if loaded_version < required_version {
Err(DriverLoadError::VersionError {
found: loaded_str.to_string(),
required: required_str.to_string(),
})
} else {
Ok(())
}
}
}
pub type ArcDriver = Arc<dyn PicoDriver>;
pub(crate) fn get_version_string(input: &str) -> String {
input
.split(|s| s == ' ' || s == ',')
.last()
.expect("Invalid version string")
.to_string()
}
pub(crate) fn parse_enum_result(buffer: &[i8], len: usize) -> Vec<EnumerationResult> {
let serials_list = buffer.from_pico_i8_string(len);
serials_list
.split(',')
.map(String::from)
.map(|device| {
let parts = device.split('[').collect::<Vec<_>>();
EnumerationResult {
serial: parts[0].to_string(),
variant: parts[1].trim_end_matches(']').to_string(),
}
})
.collect()
}
fn get_min_required_version(driver: Driver) -> &'static str {
match driver {
Driver::PS2000 => "3.0.30.1878",
Driver::PS2000A
| Driver::PS3000A
| Driver::PS4000
| Driver::PS4000A
| Driver::PS5000A
| Driver::PS6000 => "2.1.30.1878",
Driver::PS6000A => "1.0.54.2438",
_ => panic!(
"We don't know the minimum required version for the {:?} driver!",
driver,
),
}
}