use thiserror::Error;
use visa_rs::{io_to_vs_err, AsResourceManager};
pub use visa_rs::{DefaultRM, Instrument};
#[derive(Debug, Clone, Copy, Error)]
pub enum Error {
#[error(transparent)]
VisaRs(#[from] visa_rs::Error),
}
pub type Result<T> = std::result::Result<T, Error>;
#[derive(Clone, Debug)]
pub struct Idn {
pub manufacturer: String,
pub model: String,
pub serial_number: String,
pub software_version: String,
}
#[derive(Debug, PartialEq, strum::AsRefStr, strum::Display)]
pub enum Commands {
#[strum(serialize = "CLS")]
ClearStatus,
#[strum(serialize = "*ESE")]
EventStatusEnable,
#[strum(serialize = "*ESE?")]
EventStatusEnableQuery,
#[strum(serialize = "*ESR?")]
EventStatusEnableRegister,
#[strum(serialize = "*IDN?")]
Identify,
#[strum(serialize = "*OPC")]
OperationCompleteCommand,
#[strum(serialize = "*OPC?")]
OperationCompleteQuery,
#[strum(serialize = "*OPT?")]
IdentifyOptionsQuery,
#[strum(serialize = "*RST")]
Reset,
#[strum(serialize = "*SRE")]
ServiceRequestEnable,
#[strum(serialize = "*SRE?")]
ServiceRequestEnableQuery,
#[strum(serialize = "*STB?")]
StatusByteQuery,
#[strum(serialize = "*TST?")]
ResultOfSelfTestQuery,
#[strum(serialize = "*WAI")]
Wait,
}
pub trait Visa {
fn write(&mut self, command: &str) -> Result<()>;
fn read(&self) -> Result<String>;
fn read_idn(&mut self) -> Result<Idn>;
fn reset(&mut self) -> Result<()>;
fn read_resources(rm: &visa_rs::DefaultRM) -> Result<Vec<visa_rs::VisaString>>;
fn new_session(
rm: &visa_rs::DefaultRM,
manufacturer: &str,
model: &str,
) -> Result<Option<Self>>
where
Self: Sized;
fn wait_operation_complete(&mut self) -> Result<()>;
}
impl Visa for Instrument {
fn write(&mut self, command: &str) -> Result<()> {
std::io::Write::write_all(self, command.as_bytes()).map_err(io_to_vs_err)?;
Ok(())
}
fn read(&self) -> Result<String> {
let mut r = std::io::BufReader::new(self);
let mut buf = String::new();
std::io::BufRead::read_line(&mut r, &mut buf).map_err(io_to_vs_err)?;
Ok(buf)
}
fn read_idn(&mut self) -> Result<Idn> {
self.write(Commands::Identify.as_ref())?;
let response = self.read()?;
let idn = response
.split(',')
.map(|x| x.trim().to_string())
.collect::<Vec<String>>();
Ok(Idn {
manufacturer: idn[0].to_owned(),
model: idn[1].to_owned(),
serial_number: idn[2].to_owned(),
software_version: idn[3].to_owned(),
})
}
fn reset(&mut self) -> Result<()> {
self.write(Commands::Reset.as_ref())?;
Ok(())
}
fn read_resources(rm: &visa_rs::DefaultRM) -> Result<Vec<visa_rs::VisaString>> {
let expr = visa_rs::VisaString::from(
std::ffi::CString::new("?*INSTR").expect("Failed to create C compatible String."),
);
let mut visa_rs_reslist = rm.find_res_list(&expr)?;
let mut res_exhausted = false;
let mut res: Vec<visa_rs::VisaString> = vec![];
while !res_exhausted {
let visa_rs_res = visa_rs_reslist.find_next()?;
if let Some(visa_rs_res) = visa_rs_res {
res.push(visa_rs_res);
} else {
res_exhausted = true;
};
}
Ok(res)
}
fn new_session(rm: &visa_rs::DefaultRM, manufacturer: &str, model: &str) -> Result<Option<Self>>
where
Self: Sized,
{
let resources = Self::read_resources(rm)?;
for resource in resources {
let mut session = rm.open(
&resource,
visa_rs::flags::AccessMode::NO_LOCK,
visa_rs::TIMEOUT_IMMEDIATE,
)?;
let idn = Self::read_idn(&mut session)?;
let manufacturer_and_model = format!("{} {}", idn.manufacturer, idn.model);
if manufacturer_and_model.contains(&format!("{} {}", manufacturer, model)) {
return Ok(Some(session));
}
}
Ok(None)
}
fn wait_operation_complete(&mut self) -> Result<()> {
self.write(Commands::OperationCompleteQuery.as_ref())?;
self.read()?;
Ok(())
}
}