linux_gpib_rs/
error.rs

1#[cfg(feature = "linuxgpib")]
2use crate::lowlevel::utility::{AsyncIberr, ThreadIberr};
3
4use crate::status::IbStatus;
5use std::convert::Infallible;
6use std::error::Error;
7use std::ffi::NulError;
8use std::fmt;
9use std::num::TryFromIntError;
10use std::str::Utf8Error;
11use std::string::FromUtf8Error;
12#[cfg(feature = "async-tokio")]
13use tokio::task::JoinError;
14
15pub enum IbError {
16    EDVR(i64), // In this case, we hold also ibcntl value
17    ECIC,
18    ENOL,
19    EADR,
20    EARG,
21    ESAC,
22    EABO,
23    ENEB,
24    EDMA,
25    EOIP,
26    ECAP,
27    EFSO(i64), // In this case, we hold also ibcntl value
28    EBUS,
29    ESTB,
30    ESRQ,
31    ETAB,
32    ELCK,
33    EARM,
34    EHDL,
35    ECFG,
36    EWIP,
37    ERST,
38    EPWR,
39}
40
41pub enum GpibError {
42    DriverError(IbStatus, IbError),
43    Timeout,
44    ValueError(String),
45    #[cfg(feature = "async-tokio")]
46    TokioError(JoinError),
47}
48
49impl Error for GpibError {}
50
51/// EDVR values can be troubleshooted using the ibcntl value.
52/// For NI: https://documentation.help/NI-488.2/trou4xyt.html
53pub fn edvr_description(val: i64) -> String {
54    match val {
55        0xE014002C | -535560148 => "ibcntl = 0xE014002C: a call is made with a board number that is within the range of allowed board numbers, but which has not been assigned to a GPIB interface".to_owned(),
56        0xE0140025 | -535560155 => "ibcntl = 0xE0140025: a call is made with a board number that is not within the range of allowed board numbers".to_owned(),
57        0xE0140035 | -535560139 => "ibcntl = 0XE0140035: a call is made with a device name that is not listed in the logical device templates".to_owned(),
58        0xE1080080 | -519569280 | 0xE1080081 | -519569279 => format!("ibcntl = {:x}: you are using a removable interface (for example, a GPIB-USB-HS) and you removed or ejected the interface while the software is trying to communicate with it", val),
59        0xE00A0047 | -536215481 => "ibcntl = 0xE00A0047: the driver encounters an access violation when attempting to access an object supplied by the user. This can happen if the user's buffer does not have appropriate read/write characteristics. For example, this error is returned if a required pointer passed to a call is NULL.".to_owned(),
60        0xE1030043 | -519897021 => "ibcntl = 0xE1030043: you have enabled DOS NI-488.2 support and attempted to run an existing DOS NI-488.2 application that was compiled with an older, unsupported DOS application interface".to_owned(),
61        0xE1060075 | -519700363 => "ibcntl = 0xE1060075: the driver is unable to communicate with a GPIB-ENET/100 during an ibfind or ibdev".to_owned(),
62        0xE1060078 | -519700360 => "ibcntl = 0xE1060078: you are using a GPIB-ENET/100 and the network link is broken between the host and the GPIB-ENET/100 interface".to_owned(),
63        other => format!("unknown ibcntl value {:x}", other),
64    }
65}
66
67impl fmt::Display for GpibError {
68    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
69        match self {
70            GpibError::DriverError(status, error) => {
71                write!(f, "GpibError({}, {})", status, error)
72            }
73            GpibError::Timeout => {
74                write!(f, "Timeout")
75            }
76            GpibError::ValueError(desc) => {
77                write!(f, "ValueError({})", desc)
78            }
79            #[cfg(feature = "async-tokio")]
80            GpibError::TokioError(e) => {
81                write!(f, "Tokio Error ({})", e)
82            }
83        }
84    }
85}
86
87impl fmt::Debug for GpibError {
88    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
89        match self {
90            GpibError::DriverError(status, error) => {
91                write!(f, "GpibError({:?}, {:?})", status, error)
92            }
93            GpibError::Timeout => {
94                write!(f, "Timeout")
95            }
96            GpibError::ValueError(desc) => {
97                write!(f, "ValueError({})", desc)
98            }
99            #[cfg(feature = "async-tokio")]
100            GpibError::TokioError(e) => {
101                write!(f, "Tokio Error ({:?})", e)
102            }
103        }
104    }
105}
106
107impl fmt::Display for IbError {
108    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
109        match self {
110            IbError::EDVR(ibcntl) => {
111                write!(f, "EDVR  ({})", edvr_description(*ibcntl))
112            }
113            IbError::ECIC => {
114                write!(f, "ECIC")
115            }
116            IbError::ENOL => {
117                write!(f, "ENOL")
118            }
119            IbError::EADR => {
120                write!(f, "EADR")
121            }
122            IbError::EARG => {
123                write!(f, "EARG")
124            }
125            IbError::ESAC => {
126                write!(f, "ESAC")
127            }
128            IbError::EABO => {
129                write!(f, "EABO")
130            }
131            IbError::ENEB => {
132                write!(f, "ENEB")
133            }
134            IbError::EDMA => {
135                write!(f, "EDMA")
136            }
137            IbError::EOIP => {
138                write!(f, "EOIP")
139            }
140            IbError::ECAP => {
141                write!(f, "ECAP")
142            }
143            IbError::EFSO(ibcntl) => {
144                write!(f, "EFSO (ibcntl = {ibcntl})")
145            }
146            IbError::EBUS => {
147                write!(f, "EBUS")
148            }
149            IbError::ESTB => {
150                write!(f, "ESTB")
151            }
152            IbError::ESRQ => {
153                write!(f, "ESRQ")
154            }
155            IbError::ETAB => {
156                write!(f, "ETAB")
157            }
158            IbError::ELCK => {
159                write!(f, "ELCK")
160            }
161            IbError::EARM => {
162                write!(f, "EARM")
163            }
164            IbError::EHDL => {
165                write!(f, "EHDL")
166            }
167            IbError::ECFG => {
168                write!(f, "WCFG")
169            }
170            IbError::EWIP => {
171                write!(f, "EWIP")
172            }
173            IbError::ERST => {
174                write!(f, "ERST")
175            }
176            IbError::EPWR => {
177                write!(f, "EPWR")
178            }
179        }
180    }
181}
182
183impl fmt::Debug for IbError {
184    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
185        match self {
186            IbError::EDVR(ibcntl) => {
187                write!(
188                    f,
189                    "EDVR  (A system call has failed. {})",
190                    edvr_description(*ibcntl)
191                )
192            }
193            IbError::ECIC => {
194                write!(
195                    f,
196                    "ECIC (Your interface board needs to be controller-in-charge, but is not)"
197                )
198            }
199            IbError::ENOL => {
200                write!(
201                    f,
202                    "ENOL (You have attempted to write data or command bytes, but there are no listeners currently addressed)"
203                )
204            }
205            IbError::EADR => {
206                write!(
207                    f,
208                    "EADR (The interface board has failed to address itself properly before starting an io operation)"
209                )
210            }
211            IbError::EARG => {
212                write!(
213                    f,
214                    "EARG (One or more arguments to the function call were invalid)"
215                )
216            }
217            IbError::ESAC => {
218                write!(
219                    f,
220                    "ESAC (The interface board needs to be system controller, but is not)"
221                )
222            }
223            IbError::EABO => {
224                write!(
225                    f,
226                    "EABO (A read or write of data bytes has been aborted, possibly due to a timeout or reception of a device clear command)"
227                )
228            }
229            IbError::ENEB => {
230                write!(
231                    f,
232                    "ENEB (The GPIB interface board does not exist, its driver is not loaded, or it is not configured properly)"
233                )
234            }
235            IbError::EDMA => {
236                write!(
237                    f,
238                    "EDMA (Not used DMA error, included for compatibility purposes)"
239                )
240            }
241            IbError::EOIP => {
242                write!(
243                    f,
244                    "EOIP (Function call can not proceed due to an asynchronous IO operation in progress)"
245                )
246            }
247            IbError::ECAP => {
248                write!(
249                    f,
250                    "ECAP (incapable of executing function call, due the GPIB board lacking the capability, or the capability being disabled in software)"
251                )
252            }
253            IbError::EFSO(ibcntl) => {
254                write!(f, "EFSO (file system error, ibcntl = {ibcntl})")
255            }
256            IbError::EBUS => {
257                write!(
258                    f,
259                    "EBUS (an attempt to write command bytes to the bus has timed out)"
260                )
261            }
262            IbError::ESTB => {
263                write!(
264                    f,
265                    "ESTB (one or more serial poll status bytes have been lost. This can occur due to too many status bytes accumulating, through automatic serial polling, without being read)"
266                )
267            }
268            IbError::ESRQ => {
269                write!(
270                    f,
271                    "ESRQ (the serial poll request service line is stuck on. This can occur if a physical device on the bus requests service, but its GPIB address has not been opened by any process. Thus the automatic serial polling routines are unaware of the device's existence and will never serial poll it)"
272                )
273            }
274            IbError::ETAB => {
275                write!(
276                    f,
277                    "ETAB (this error can be returned by ibevent(), FindLstn(), or FindRQS(). See their descriptions for more information)"
278                )
279            }
280            IbError::ELCK => {
281                write!(f, "ELCK (Address or board is locked)")
282            }
283            IbError::EARM => {
284                write!(f, "EARM (The ibnotify Callback failed to rearm)")
285            }
286            IbError::EHDL => {
287                write!(f, "EHDL (The input handle is invalid)",)
288            }
289            IbError::ECFG => {
290                write!(f, "WCFG (Configuration warning)",)
291            }
292            IbError::EWIP => {
293                write!(f, "EWIP (Wait already in progress on input ud)")
294            }
295            IbError::ERST => {
296                write!(
297                    f,
298                    "ERST (The event notification was cancelled due to a reset of the interface)"
299                )
300            }
301            IbError::EPWR => {
302                write!(
303                    f,
304                    "EPWR (The system or board has lost power or gone to standby)"
305                )
306            }
307        }
308    }
309}
310
311impl IbError {
312    /// Create IbError from iberr value
313    pub fn from_iberr(iberr: linux_gpib_sys::iberr_type) -> Result<IbError, GpibError> {
314        match iberr {
315            #[cfg(feature = "linuxgpib")]
316            0 => Ok(IbError::EDVR(unsafe { linux_gpib_sys::ibcntl })),
317            #[cfg(feature = "nigpib")]
318            0 => Ok(IbError::EDVR(unsafe { linux_gpib_sys::Ibcnt().into() })),
319            1 => Ok(IbError::ECIC),
320            2 => Ok(IbError::ENOL),
321            3 => Ok(IbError::EADR),
322            4 => Ok(IbError::EARG),
323            5 => Ok(IbError::ESAC),
324            6 => Ok(IbError::EABO),
325            7 => Ok(IbError::ENEB),
326            8 => Ok(IbError::EDMA),
327            10 => Ok(IbError::EOIP),
328            11 => Ok(IbError::ECAP),
329            #[cfg(feature = "linuxgpib")]
330            12 => Ok(IbError::EFSO(unsafe { linux_gpib_sys::ibcntl })),
331            #[cfg(feature = "nigpib")]
332            12 => Ok(IbError::EFSO(unsafe { linux_gpib_sys::Ibcnt().into() })),
333            14 => Ok(IbError::EBUS),
334            15 => Ok(IbError::ESTB),
335            16 => Ok(IbError::ESRQ),
336            20 => Ok(IbError::ETAB),
337            21 => Ok(IbError::ELCK),
338            22 => Ok(IbError::EARM),
339            23 => Ok(IbError::EHDL),
340            24 => Ok(IbError::ECFG),
341            26 => Ok(IbError::EWIP),
342            27 => Ok(IbError::ERST),
343            28 => Ok(IbError::EPWR),
344            other => Err(GpibError::ValueError(format!(
345                "Unexpected iberr value = {}.",
346                other
347            ))),
348        }
349    }
350
351    /// Create IbError from current Linux-GPIB global iberr variable
352    pub unsafe fn current_global_error() -> Result<IbError, GpibError> {
353        let status = unsafe { IbStatus::current_global_status() };
354        if status.err {
355            #[cfg(feature = "linuxgpib")]
356            return IbError::from_iberr(unsafe { linux_gpib_sys::iberr });
357            #[cfg(feature = "nigpib")]
358            return IbError::from_iberr(unsafe { linux_gpib_sys::Iberr() });
359        } else {
360            Err(GpibError::ValueError(format!(
361                "Unable to get error because is not ERR (status = {:?})",
362                status
363            )))
364        }
365    }
366
367    #[cfg(feature = "linuxgpib")]
368    /// Create IbError from current thread-local iberr value
369    pub fn current_thread_local_error() -> Result<IbError, GpibError> {
370        let status = IbStatus::current_thread_local_status();
371        if status.err {
372            IbError::from_iberr(ThreadIberr())
373        } else {
374            Err(GpibError::ValueError(format!(
375                "Unable to get error because is not ERR (status = {:?})",
376                status
377            )))
378        }
379    }
380
381    #[cfg(feature = "linuxgpib")]
382    /// Create IbError for last asynchronous I/O operation
383    pub fn current_async_local_error() -> Result<IbError, GpibError> {
384        let status = IbStatus::current_async_local_status();
385        if status.err {
386            IbError::from_iberr(AsyncIberr())
387        } else {
388            Err(GpibError::ValueError(format!(
389                "Unable to get error because is not ERR (status = {:?})",
390                status
391            )))
392        }
393    }
394}
395
396impl From<NulError> for GpibError {
397    fn from(e: NulError) -> GpibError {
398        GpibError::ValueError(format!("{:?}", e))
399    }
400}
401
402impl From<TryFromIntError> for GpibError {
403    fn from(e: TryFromIntError) -> GpibError {
404        GpibError::ValueError(format!("{:?}", e,))
405    }
406}
407
408impl From<FromUtf8Error> for GpibError {
409    fn from(e: FromUtf8Error) -> GpibError {
410        GpibError::ValueError(format!("{:?}", e,))
411    }
412}
413
414impl From<Utf8Error> for GpibError {
415    fn from(e: Utf8Error) -> GpibError {
416        GpibError::ValueError(format!("{:?}", e,))
417    }
418}
419
420impl From<Infallible> for GpibError {
421    fn from(e: Infallible) -> GpibError {
422        GpibError::ValueError(e.to_string())
423    }
424}
425
426#[cfg(feature = "async-tokio")]
427impl From<JoinError> for GpibError {
428    fn from(e: JoinError) -> GpibError {
429        GpibError::TokioError(e)
430    }
431}