linux_gpib_rs/
instrument.rs

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