use crate::error::{GpibError, IbError};
use crate::lowlevel::multidevice;
#[cfg(feature = "async-tokio")]
use crate::lowlevel::traditional::ibwait;
use crate::lowlevel::traditional::{ibclr, ibdev, ibonl, ibrd, ibrda, ibtmo, ibwrt, ibwrta};
use crate::lowlevel::utility::Addr4882;
use crate::status::IbStatus;
use crate::types::{IbEosMode, IbOnline, IbSendEOI, IbTimeout, PrimaryAddress, SecondaryAddress};
use std::default::Default;
use std::fmt;
use std::os::raw::c_int;
use std::time::Duration;
pub struct Parameters {
pub timeout: IbTimeout,
pub send_eoi: IbSendEOI,
pub eos_mode: IbEosMode,
}
impl Default for Parameters {
fn default() -> Self {
Self {
timeout: IbTimeout::T1s,
send_eoi: IbSendEOI::default(),
eos_mode: IbEosMode::default(),
}
}
}
#[derive(Clone, PartialEq)]
pub struct Board {
board_number: c_int,
}
#[derive(Clone)]
pub struct Instrument {
board: Board,
addr: Addr4882,
}
pub struct InstrumentHandle {
ud: c_int,
}
impl Board {
pub fn with_board_number(board_number: c_int) -> Self {
Board {
board_number: board_number,
}
}
pub fn clear_devices(&self, instruments: &Vec<Instrument>) -> Result<(), GpibError> {
if instruments
.iter()
.any(|instr| instr.board.board_number != self.board_number)
{
return Err(GpibError::ValueError(
"clear_devices can only send to devices belonging to this board.".to_owned(),
));
}
let address_list = instruments.iter().map(|instr| instr.addr).collect();
multidevice::DevClearList(self.board_number, &address_list)
}
pub fn interface_clear(&self) -> Result<(), GpibError> {
multidevice::SendIFC(self.board_number)
}
pub fn find_listeners(&self) -> Result<Vec<Instrument>, GpibError> {
Ok(multidevice::FindAllLstn(self.board_number)?
.into_iter()
.map(|addr| Instrument {
board: self.clone(),
addr: addr,
})
.collect())
}
pub fn send_list(
&self,
instruments: &Vec<Instrument>,
data: &[u8],
mode: IbSendEOI,
) -> Result<(), GpibError> {
if instruments
.iter()
.any(|instr| instr.board.board_number != self.board_number)
{
return Err(GpibError::ValueError(
"clear_devices can only send to devices belonging to this board.".to_owned(),
));
}
let address_list = instruments.iter().map(|instr| instr.addr).collect();
multidevice::SendList(self.board_number, &address_list, data, mode)
}
}
impl Default for Board {
fn default() -> Self {
Board::with_board_number(0)
}
}
impl fmt::Display for Board {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Board({})", self.board_number)
}
}
impl fmt::Debug for Board {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Board({})", self.board_number)
}
}
impl Instrument {
pub fn send(&self, data: &[u8], mode: IbSendEOI) -> Result<(), GpibError> {
multidevice::Send(self.board.board_number, self.addr, data, mode)
}
pub fn receive(&self) -> Result<String, GpibError> {
const BUFFER_SIZE: usize = 1024;
let mut result: Vec<u8> = Vec::new();
loop {
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let (status, n_read) = multidevice::Receive(
self.board.board_number,
self.addr,
&mut buffer,
linux_gpib_sys::STOPend,
)?;
if n_read > 0 {
result.extend(buffer[0..n_read].to_vec());
}
if status.end || n_read < BUFFER_SIZE || n_read == 0 {
break;
}
}
let answer = String::from_utf8(result)?;
Ok(answer)
}
pub fn query(&self, data: &str) -> Result<String, GpibError> {
self.send(data.as_bytes(), IbSendEOI::default())?;
self.receive()
}
pub fn from_visa_string(address: &str) -> Result<Self, GpibError> {
let v: Vec<&str> = address.split("::").collect();
if v.len() < 2 {
return Err(GpibError::ValueError(format!(
"Invalid address '{}'.",
address
)));
}
if v[0].starts_with("GPIB") {
let (_, board_number) = v[0].split_at(4);
let board_number = i32::from_str_radix(board_number, 10).map_err(|e| {
GpibError::ValueError(format!(
"Unable to parse GPIB Board index from string '{}' ({:?})",
board_number, e,
))
})?;
let primary_address = i32::from_str_radix(v[1], 10).map_err(|e| {
GpibError::ValueError(format!(
"Unable to parse GPIB primary address from string '{}' ({:?})",
v[1], e,
))
})?;
Ok(Self {
board: Board::with_board_number(board_number),
addr: Addr4882::new(
PrimaryAddress::new(primary_address)?,
SecondaryAddress::default(),
)?,
})
} else {
Err(GpibError::ValueError(
"Address is expected as GPIBN::primary_address::INSTR".to_owned(),
))
}
}
pub fn visa_string(&self) -> String {
format!(
"GPIB{}::{}::INSTR",
self.board.board_number,
self.addr.pad(),
)
}
pub fn open(&self, params: Parameters) -> Result<InstrumentHandle, GpibError> {
let ud = ibdev(
self.board.board_number,
self.addr.primary_address()?,
self.addr.secondary_address()?,
params.timeout,
params.send_eoi,
params.eos_mode,
)?;
ibclr(ud)?;
Ok(InstrumentHandle { ud })
}
}
impl fmt::Display for Instrument {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.visa_string())
}
}
impl fmt::Debug for Instrument {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Instrument({:?}, {:?})", self.board, self.addr)
}
}
impl InstrumentHandle {
pub fn blocking_read_raw(&self) -> Result<Vec<u8>, GpibError> {
const BUFFER_SIZE: usize = 1024;
let mut result: Vec<u8> = Vec::new();
loop {
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let (status, n_read) = ibrd(self.ud, &mut buffer)?;
if n_read > 0 {
result.extend(buffer[0..n_read].to_vec());
}
if status.end || n_read < BUFFER_SIZE || n_read == 0 {
break;
}
}
Ok(result)
}
pub fn blocking_read(&self) -> Result<String, GpibError> {
let result = self.blocking_read_raw()?;
let answer = String::from_utf8(result)?;
Ok(answer)
}
#[cfg(feature = "async-tokio")]
pub async fn read_raw(&self) -> Result<Vec<u8>, GpibError> {
const BUFFER_SIZE: usize = 1024;
let mut result: Vec<u8> = Vec::new();
loop {
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
unsafe { ibrda(self.ud, &mut buffer) }?;
let (status, n_read) = ibwait(
self.ud,
IbStatus::default()
.with_timo(true)
.with_cmpl(true)
.with_end(true),
)
.await?;
if status.err {
return Err(GpibError::DriverError(
status,
#[cfg(feature = "linuxgpib")]
IbError::current_thread_local_error()?,
#[cfg(feature = "nigpib")]
unsafe { IbError::current_global_error() }?,
));
} else if status.timo {
return Err(GpibError::Timeout);
}
log::debug!("read({}) -> {} bytes read.", self.ud, n_read);
if n_read > 0 {
result.extend(buffer[0..n_read].to_vec());
}
if status.end || n_read < BUFFER_SIZE || n_read == 0 {
break;
}
}
Ok(result)
}
#[cfg(feature = "async-tokio")]
pub async fn read(&self) -> Result<String, GpibError> {
let result = self.read_raw().await?;
let answer = String::from_utf8(result)?;
Ok(answer)
}
pub fn blocking_write(&self, data: &str) -> Result<(), GpibError> {
let _n_written = ibwrt(self.ud, data.as_bytes())?;
Ok(())
}
#[cfg(feature = "async-tokio")]
pub async fn write(&self, data: &str) -> Result<(), GpibError> {
let data = data.as_bytes();
unsafe { ibwrta(self.ud, data) }?;
let (status, _count) = ibwait(
self.ud,
IbStatus::default()
.with_timo(true)
.with_cmpl(true)
.with_end(true)
.with_rqs(true),
)
.await?;
if status.err {
Err(GpibError::DriverError(
status,
#[cfg(feature = "linuxgpib")]
IbError::current_thread_local_error()?,
#[cfg(feature = "nigpib")]
unsafe { IbError::current_global_error() }?,
))
} else if status.timo {
Err(GpibError::Timeout)
} else if status.cmpl || status.end {
Ok(())
} else {
Err(GpibError::ValueError(format!(
"Unexpected status after waiting: {:?}",
status
)))
}
}
pub fn blocking_query(&self, data: &str) -> Result<String, GpibError> {
self.blocking_write(data)?;
self.blocking_read()
}
#[cfg(feature = "async-tokio")]
pub async fn query(&self, data: &str) -> Result<String, GpibError> {
self.write(data).await?;
self.read().await
}
pub fn clear(&self) -> Result<(), GpibError> {
ibclr(self.ud)
}
pub fn set_timeout(&self, timeout: Duration) -> Result<(), GpibError> {
let tmo = IbTimeout::closest_from(timeout);
ibtmo(self.ud, tmo)
}
}
impl Drop for InstrumentHandle {
fn drop(&mut self) {
match ibonl(self.ud, IbOnline::Close) {
Ok(()) => {}
Err(e) => {
println!("Error while closing (ud = {}): {:?}", self.ud, e);
}
}
}
}
impl fmt::Display for InstrumentHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.ud)
}
}
impl fmt::Debug for InstrumentHandle {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "InstrumentHandle({})", self.ud)
}
}