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