linux_gpib_rs/
instrument.rs

1use crate::error::{GpibError, IbError};
2use crate::lowlevel::multidevice;
3use crate::lowlevel::traditional::{ibclr, ibdev, ibonl, ibrd, ibrda, ibwait, ibwrt, ibwrta};
4use crate::lowlevel::utility::Addr4882;
5use crate::status::IbStatus;
6use crate::types::{IbEosMode, IbOnline, IbSendEOI, IbTimeout, PrimaryAddress, SecondaryAddress};
7use crate::DEBUG;
8use std::default::Default;
9use std::fmt;
10use std::os::raw::c_int;
11
12pub struct Parameters {
13    pub timeout: IbTimeout,
14    pub send_eoi: IbSendEOI,
15    pub eos_mode: IbEosMode,
16}
17
18impl Default for Parameters {
19    fn default() -> Self {
20        Self {
21            timeout: IbTimeout::T1s,
22            send_eoi: IbSendEOI::default(),
23            eos_mode: IbEosMode::default(),
24        }
25    }
26}
27
28#[derive(Clone, PartialEq)]
29pub struct Board {
30    board_number: c_int,
31}
32
33#[derive(Clone)]
34pub struct Instrument {
35    board: Board,
36    addr: Addr4882,
37}
38
39pub struct InstrumentHandle {
40    ud: c_int,
41}
42
43impl Board {
44    pub fn with_board_number(board_number: c_int) -> Self {
45        Board {
46            board_number: board_number,
47        }
48    }
49
50    /// clear devices
51    pub fn clear_devices(&self, instruments: &Vec<Instrument>) -> Result<(), GpibError> {
52        if instruments
53            .iter()
54            .any(|instr| instr.board.board_number != self.board_number)
55        {
56            return Err(GpibError::ValueError(
57                "clear_devices can only send to devices belonging to this board.".to_owned(),
58            ));
59        }
60        let address_list = instruments.iter().map(|instr| instr.addr).collect();
61        multidevice::DevClearList(self.board_number, &address_list)
62    }
63
64    /// perform interface clear.
65    /// The interface clear causes all devices to untalk and unlisten, puts them into serial poll disabled state
66    /// (don't worry, you will still be able to conduct serial polls), and the board becomes controller-in-charge.
67    pub fn interface_clear(&self) -> Result<(), GpibError> {
68        multidevice::SendIFC(self.board_number)
69    }
70
71    /// find listeners on the board
72    pub fn find_listeners(&self) -> Result<Vec<Instrument>, GpibError> {
73        Ok(multidevice::FindAllLstn(self.board_number)?
74            .into_iter()
75            .map(|addr| Instrument {
76                board: self.clone(),
77                addr: addr,
78            })
79            .collect())
80    }
81
82    /// write data to multiple devices
83    pub fn send_list(
84        &self,
85        instruments: &Vec<Instrument>,
86        data: &[u8],
87        mode: IbSendEOI,
88    ) -> Result<(), GpibError> {
89        if instruments
90            .iter()
91            .any(|instr| instr.board.board_number != self.board_number)
92        {
93            return Err(GpibError::ValueError(
94                "clear_devices can only send to devices belonging to this board.".to_owned(),
95            ));
96        }
97        let address_list = instruments.iter().map(|instr| instr.addr).collect();
98        multidevice::SendList(self.board_number, &address_list, data, mode)
99    }
100}
101
102impl Default for Board {
103    fn default() -> Self {
104        Board::with_board_number(0)
105    }
106}
107
108impl fmt::Display for Board {
109    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
110        write!(f, "Board({})", self.board_number)
111    }
112}
113
114impl fmt::Debug for Board {
115    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
116        write!(f, "Board({})", self.board_number)
117    }
118}
119
120impl Instrument {
121    /// Send data to the instrument with the multidevice 488.2 API
122    pub fn send(&self, data: &[u8], mode: IbSendEOI) -> Result<(), GpibError> {
123        multidevice::Send(self.board.board_number, self.addr, data, mode)
124    }
125
126    /// Receive data from the instrument with the multidevice 488.2 API
127    pub fn receive(&self) -> Result<String, GpibError> {
128        const BUFFER_SIZE: usize = 1024;
129        let mut result: Vec<u8> = Vec::new();
130        loop {
131            let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
132            let (status, n_read) = multidevice::Receive(
133                self.board.board_number,
134                self.addr,
135                &mut buffer,
136                linux_gpib_sys::STOPend,
137            )?;
138            if n_read > 0 {
139                result.extend(buffer[0..n_read].to_vec());
140            }
141            if status.end || n_read < BUFFER_SIZE || n_read == 0 {
142                break;
143            }
144        }
145        let answer = String::from_utf8(result)?;
146        Ok(answer)
147    }
148
149    /// Performs send and receive
150    pub fn query(&self, data: &str) -> Result<String, GpibError> {
151        self.send(data.as_bytes(), IbSendEOI::default())?;
152        self.receive()
153    }
154
155    /// Create Instrument from a VISA string
156    pub fn from_visa_string(address: &str) -> Result<Self, GpibError> {
157        let v: Vec<&str> = address.split("::").collect();
158        if v.len() < 2 {
159            return Err(GpibError::ValueError(format!(
160                "Invalid address '{}'.",
161                address
162            )));
163        }
164        if v[0].starts_with("GPIB") {
165            let (_, board_number) = v[0].split_at(4);
166            let board_number = i32::from_str_radix(board_number, 10).map_err(|e| {
167                GpibError::ValueError(format!(
168                    "Unable to parse GPIB Board index from string '{}' ({:?})",
169                    board_number, e,
170                ))
171            })?;
172            let primary_address = i32::from_str_radix(v[1], 10).map_err(|e| {
173                GpibError::ValueError(format!(
174                    "Unable to parse GPIB primary address from string '{}' ({:?})",
175                    v[1], e,
176                ))
177            })?;
178            Ok(Self {
179                board: Board::with_board_number(board_number),
180                addr: Addr4882::new(
181                    PrimaryAddress::new(primary_address)?,
182                    SecondaryAddress::default(),
183                )?,
184            })
185        } else {
186            Err(GpibError::ValueError(
187                "Address is expected as GPIBN::primary_address::INSTR".to_owned(),
188            ))
189        }
190    }
191
192    /// Create VISA string from board and address
193    pub fn visa_string(&self) -> String {
194        format!(
195            "GPIB{}::{}::INSTR",
196            self.board.board_number,
197            self.addr.pad(),
198        )
199    }
200
201    /// Open with the traditional 488.1 API
202    pub fn open(&self, params: Parameters) -> Result<InstrumentHandle, GpibError> {
203        let ud = ibdev(
204            self.board.board_number,
205            self.addr.primary_address()?,
206            self.addr.secondary_address()?,
207            params.timeout,
208            params.send_eoi,
209            params.eos_mode,
210        )?;
211        ibclr(ud)?;
212        Ok(InstrumentHandle { ud })
213    }
214}
215
216impl fmt::Display for Instrument {
217    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
218        write!(f, "{}", self.visa_string())
219    }
220}
221
222impl fmt::Debug for Instrument {
223    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
224        write!(f, "Instrument({:?}, {:?})", self.board, self.addr)
225    }
226}
227
228impl InstrumentHandle {
229    pub fn blocking_read(&self) -> Result<String, GpibError> {
230        const BUFFER_SIZE: usize = 1024;
231        let mut result: Vec<u8> = Vec::new();
232        loop {
233            let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
234            let (status, n_read) = ibrd(self.ud, &mut buffer)?;
235            if n_read > 0 {
236                result.extend(buffer[0..n_read].to_vec());
237            }
238            if status.end || n_read < BUFFER_SIZE || n_read == 0 {
239                break;
240            }
241        }
242        let answer = String::from_utf8(result)?;
243        Ok(answer)
244    }
245
246    #[cfg(feature = "async-tokio")]
247    pub async fn read(&self) -> Result<String, GpibError> {
248        const BUFFER_SIZE: usize = 1024;
249        let mut result: Vec<u8> = Vec::new();
250        loop {
251            let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
252            unsafe { ibrda(self.ud, &mut buffer) }?;
253            let (status, n_read) = ibwait(
254                self.ud,
255                IbStatus::default()
256                    .with_timo(true)
257                    .with_cmpl(true)
258                    .with_end(true),
259            )
260            .await?;
261            if status.err {
262                return Err(GpibError::DriverError(
263                    status,
264                    IbError::current_thread_local_error()?,
265                ));
266            } else if status.timo {
267                return Err(GpibError::Timeout);
268            }
269            if DEBUG {
270                println!("read({}) -> {} bytes read.", self.ud, n_read);
271            }
272            if n_read > 0 {
273                result.extend(buffer[0..n_read].to_vec());
274            }
275            if status.end || n_read < BUFFER_SIZE || n_read == 0 {
276                break;
277            }
278        }
279        let answer = String::from_utf8(result)?;
280        Ok(answer)
281    }
282
283    pub fn blocking_write(&self, data: &str) -> Result<(), GpibError> {
284        let _n_written = ibwrt(self.ud, data.as_bytes())?;
285        Ok(())
286    }
287
288    #[cfg(feature = "async-tokio")]
289    pub async fn write(&self, data: &str) -> Result<(), GpibError> {
290        let data = data.as_bytes();
291        unsafe { ibwrta(self.ud, data) }?;
292        let (status, _count) = ibwait(
293            self.ud,
294            IbStatus::default()
295                .with_timo(true)
296                .with_cmpl(true)
297                .with_end(true)
298                .with_rqs(true),
299        )
300        .await?;
301        if status.err {
302            Err(GpibError::DriverError(
303                status,
304                IbError::current_thread_local_error()?,
305            ))
306        } else if status.timo {
307            Err(GpibError::Timeout)
308        } else if status.cmpl || status.end {
309            Ok(())
310        } else {
311            Err(GpibError::ValueError(format!(
312                "Unexpected status after waiting: {:?}",
313                status
314            )))
315        }
316    }
317
318    pub fn blocking_query(&self, data: &str) -> Result<String, GpibError> {
319        self.blocking_write(data)?;
320        self.blocking_read()
321    }
322
323    #[cfg(feature = "async-tokio")]
324    pub async fn query(&self, data: &str) -> Result<String, GpibError> {
325        self.write(data).await?;
326        self.read().await
327    }
328}
329
330impl Drop for InstrumentHandle {
331    fn drop(&mut self) {
332        match ibonl(self.ud, IbOnline::Close) {
333            Ok(()) => {}
334            Err(e) => {
335                println!("Error while closing (ud = {}): {:?}", self.ud, e);
336            }
337        }
338    }
339}
340
341impl fmt::Display for InstrumentHandle {
342    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
343        write!(f, "{}", self.ud)
344    }
345}
346
347impl fmt::Debug for InstrumentHandle {
348    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
349        write!(f, "InstrumentHandle({})", self.ud)
350    }
351}