use std::fmt::Display;
use thiserror::Error;
use visa_rs::{flags::AccessMode, AsResourceManager, DefaultRM, TIMEOUT_IMMEDIATE};
mod prelude;
#[derive(Debug, Error)]
pub enum Error {
#[error(transparent)]
StdIo(#[from] std::io::Error),
#[error(transparent)]
VisaRs(#[from] visa_rs::Error),
#[error("No instrument was found")]
NoInstrumentFound(),
}
pub type Result<T> = core::result::Result<T, Error>;
#[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,
}
#[derive(Clone, Debug)]
pub struct Idn {
pub manufacturer: String,
pub model: String,
pub serial_number: String,
pub software_version: String,
}
pub fn find_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 get_idn(instrument: &mut visa_rs::Instrument) -> Result<Idn> {
std::io::Write::write_all(instrument, Commands::Identify.as_ref().as_bytes())?;
let mut r = std::io::BufReader::new(instrument);
let mut buf = String::new();
std::io::BufRead::read_line(&mut r, &mut buf)?;
let buf = buf
.split(',')
.map(|s| s.trim().to_string())
.collect::<Vec<String>>();
Ok(Idn {
manufacturer: buf[0].to_owned(),
model: buf[1].to_owned(),
serial_number: buf[2].to_owned(),
software_version: buf[3].to_owned(),
})
}
pub trait Visa {
fn into_inner(&self) -> &visa_rs::Instrument;
fn find_instrument<T>(rm: &DefaultRM, manufacturer: T, model: T) -> Result<visa_rs::Instrument>
where
T: Into<String> + Display,
{
let resources = find_resources(rm)?;
for resource in resources {
let mut instrument = rm.open(&resource, AccessMode::NO_LOCK, TIMEOUT_IMMEDIATE)?;
let idn = get_idn(&mut instrument).unwrap();
let manufacturer_and_model = format!("{} {}", idn.manufacturer, idn.model);
if manufacturer_and_model.contains(&format!("{} {}", manufacturer, model)) {
return Ok(instrument);
}
}
Err(Error::NoInstrumentFound())
}
fn write<T>(&mut self, cmd: T) -> Result<()>
where
T: Into<String>,
{
let cmd: String = cmd.into();
std::io::Write::write_all(&mut self.into_inner(), cmd.as_bytes())?;
Ok(())
}
fn read(&self) -> Result<String> {
let mut buf = String::new();
std::io::Read::read_to_string(&mut self.into_inner(), &mut buf)?;
Ok(buf)
}
fn get_idn(&mut self) -> Result<Idn> {
self.write(Commands::Identify.as_ref())?;
let buf = self.read()?;
let buf = buf
.split(',')
.map(|s| s.trim().to_string())
.collect::<Vec<String>>();
Ok(Idn {
manufacturer: buf[0].to_owned(),
model: buf[1].to_owned(),
serial_number: buf[2].to_owned(),
software_version: buf[3].to_owned(),
})
}
fn reset(&mut self) -> Result<()> {
self.write(Commands::Reset.as_ref())?;
Ok(())
}
}