Skip to main content

instrument_core/
connect.rs

1use std::time::Duration;
2
3/// VISA access mode (maps to visa-rs AccessMode when using visa backend).
4#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
5pub struct AccessMode {
6    pub exclusive_lock: bool,
7    pub shared_lock: bool,
8}
9
10impl AccessMode {
11    pub const NO_LOCK: Self = Self {
12        exclusive_lock: false,
13        shared_lock: false,
14    };
15
16    pub const SHARED_LOCK: Self = Self {
17        exclusive_lock: false,
18        shared_lock: true,
19    };
20
21    pub const EXCLUSIVE_LOCK: Self = Self {
22        exclusive_lock: true,
23        shared_lock: false,
24    };
25}
26
27/// Options when opening an instrument session.
28#[derive(Debug, Clone)]
29pub struct ConnectOptions {
30    pub open_timeout: Duration,
31    pub read_timeout: Duration,
32    pub write_timeout: Duration,
33    pub per_op_timeout: Option<Duration>,
34    pub terminator: String,
35    pub access_mode: AccessMode,
36    pub reset_on_connect: bool,
37    pub retries: u32,
38    pub retry_backoff: Duration,
39    /// When true, timed-out writes/queries call [`crate::transport::Transport::reconnect`].
40    ///
41    /// VISA backends return [`crate::error::Error::Unsupported`]; `try_reconnect` swallows
42    /// that so write/query retry still runs. TCP reconnect needs a SOCKET transport
43    /// (not VISA). Default stays true so a future reconnectable transport is opted in.
44    pub reconnect_on_failure: bool,
45}
46
47impl Default for ConnectOptions {
48    fn default() -> Self {
49        Self {
50            open_timeout: Duration::from_secs(5),
51            read_timeout: Duration::from_secs(10),
52            write_timeout: Duration::from_secs(10),
53            per_op_timeout: None,
54            terminator: "\n".to_string(),
55            access_mode: AccessMode::NO_LOCK,
56            reset_on_connect: false,
57            retries: 2,
58            retry_backoff: Duration::from_millis(100),
59            reconnect_on_failure: true,
60        }
61    }
62}
63
64impl ConnectOptions {
65    pub fn with_read_timeout(mut self, timeout: Duration) -> Self {
66        self.read_timeout = timeout;
67        self
68    }
69
70    pub fn with_per_op_timeout(mut self, timeout: Duration) -> Self {
71        self.per_op_timeout = Some(timeout);
72        self
73    }
74
75    /// Single I/O timeout for backends (VISA) that cannot split read vs write.
76    pub fn io_timeout(&self) -> Duration {
77        self.per_op_timeout
78            .unwrap_or_else(|| self.read_timeout.max(self.write_timeout))
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    use super::*;
85
86    #[test]
87    fn io_timeout_uses_max_of_read_and_write() {
88        let mut opts = ConnectOptions::default();
89        opts.read_timeout = Duration::from_secs(2);
90        opts.write_timeout = Duration::from_secs(7);
91        assert_eq!(opts.io_timeout(), Duration::from_secs(7));
92        opts.per_op_timeout = Some(Duration::from_millis(50));
93        assert_eq!(opts.io_timeout(), Duration::from_millis(50));
94    }
95}