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 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 pub fn interface_clear(&self) -> Result<(), GpibError> {
70 multidevice::SendIFC(self.board_number)
71 }
72
73 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 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 pub fn send(&self, data: &[u8], mode: IbSendEOI) -> Result<(), GpibError> {
125 multidevice::Send(self.board.board_number, self.addr, data, mode)
126 }
127
128 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 pub fn query(&self, data: &str) -> Result<String, GpibError> {
153 self.send(data.as_bytes(), IbSendEOI::default())?;
154 self.receive()
155 }
156
157 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 pub fn visa_string(&self) -> String {
196 format!(
197 "GPIB{}::{}::INSTR",
198 self.board.board_number,
199 self.addr.pad(),
200 )
201 }
202
203 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 pub fn clear(&self) -> Result<(), GpibError> {
348 ibclr(self.ud)
349 }
350
351 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}