Skip to main content

nanonis_rs/client/
dig_lines.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Digital port selection.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum DigitalPort {
8    /// Port A
9    #[default]
10    PortA = 0,
11    /// Port B
12    PortB = 1,
13    /// Port C
14    PortC = 2,
15    /// Port D
16    PortD = 3,
17}
18
19impl From<DigitalPort> for u32 {
20    fn from(port: DigitalPort) -> Self {
21        port as u32
22    }
23}
24
25impl From<DigitalPort> for u16 {
26    fn from(port: DigitalPort) -> Self {
27        port as u16
28    }
29}
30
31/// Digital line direction.
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
33pub enum DigitalDirection {
34    /// Input direction
35    #[default]
36    Input = 0,
37    /// Output direction
38    Output = 1,
39}
40
41impl From<DigitalDirection> for u32 {
42    fn from(dir: DigitalDirection) -> Self {
43        dir as u32
44    }
45}
46
47/// Digital line polarity.
48#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
49pub enum DigitalPolarity {
50    /// Low active
51    #[default]
52    LowActive = 0,
53    /// High active
54    HighActive = 1,
55}
56
57impl From<DigitalPolarity> for u32 {
58    fn from(pol: DigitalPolarity) -> Self {
59        pol as u32
60    }
61}
62
63/// Digital line configuration.
64#[derive(Debug, Clone, Copy)]
65pub struct DigitalLineConfig {
66    /// Digital line number (1-8)
67    pub line: u32,
68    /// Port selection
69    pub port: DigitalPort,
70    /// Line direction (input/output)
71    pub direction: DigitalDirection,
72    /// Line polarity
73    pub polarity: DigitalPolarity,
74}
75
76impl Default for DigitalLineConfig {
77    fn default() -> Self {
78        Self {
79            line: 1,
80            port: DigitalPort::PortA,
81            direction: DigitalDirection::Input,
82            polarity: DigitalPolarity::LowActive,
83        }
84    }
85}
86
87/// Pulse generator configuration.
88#[derive(Debug, Clone)]
89pub struct PulseConfig {
90    /// Port selection
91    pub port: DigitalPort,
92    /// Digital lines to pulse (1-8)
93    pub lines: Vec<u8>,
94    /// Pulse width in seconds
95    pub pulse_width_s: f32,
96    /// Pulse pause in seconds
97    pub pulse_pause_s: f32,
98    /// Number of pulses (1-32767)
99    pub num_pulses: i32,
100    /// Wait until all pulses are generated before returning
101    pub wait_until_finished: bool,
102}
103
104impl Default for PulseConfig {
105    fn default() -> Self {
106        Self {
107            port: DigitalPort::PortA,
108            lines: vec![1],
109            pulse_width_s: 0.001,
110            pulse_pause_s: 0.001,
111            num_pulses: 1,
112            wait_until_finished: true,
113        }
114    }
115}
116
117impl NanonisClient {
118    /// Configure the properties of a digital line.
119    ///
120    /// # Arguments
121    /// * `config` - A [`DigitalLineConfig`] struct with line configuration
122    ///
123    /// # Errors
124    /// Returns `NanonisError` if communication fails.
125    ///
126    /// # Examples
127    /// ```no_run
128    /// use nanonis_rs::NanonisClient;
129    /// use nanonis_rs::dig_lines::{DigitalLineConfig, DigitalPort, DigitalDirection, DigitalPolarity};
130    ///
131    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
132    /// let config = DigitalLineConfig {
133    ///     line: 1,
134    ///     port: DigitalPort::PortA,
135    ///     direction: DigitalDirection::Output,
136    ///     polarity: DigitalPolarity::HighActive,
137    /// };
138    /// client.dig_lines_props_set(&config)?;
139    /// # Ok::<(), Box<dyn std::error::Error>>(())
140    /// ```
141    pub fn dig_lines_props_set(&mut self, config: &DigitalLineConfig) -> Result<(), NanonisError> {
142        self.quick_send(
143            "DigLines.PropsSet",
144            vec![
145                NanonisValue::U32(config.line),
146                NanonisValue::U32(config.port.into()),
147                NanonisValue::U32(config.direction.into()),
148                NanonisValue::U32(config.polarity.into()),
149            ],
150            vec!["I", "I", "I", "I"],
151            vec![],
152        )?;
153        Ok(())
154    }
155
156    /// Set the status of a digital output line.
157    ///
158    /// # Arguments
159    /// * `port` - Port selection
160    /// * `line` - Digital line number (1-8)
161    /// * `active` - True for active, false for inactive
162    ///
163    /// # Errors
164    /// Returns `NanonisError` if communication fails.
165    pub fn dig_lines_out_status_set(
166        &mut self,
167        port: DigitalPort,
168        line: u32,
169        active: bool,
170    ) -> Result<(), NanonisError> {
171        let status = if active { 1u32 } else { 0u32 };
172        self.quick_send(
173            "DigLines.OutStatusSet",
174            vec![
175                NanonisValue::U32(port.into()),
176                NanonisValue::U32(line),
177                NanonisValue::U32(status),
178            ],
179            vec!["I", "I", "I"],
180            vec![],
181        )?;
182        Ok(())
183    }
184
185    /// Read the TTL voltages present at the pins of the selected port.
186    ///
187    /// # Arguments
188    /// * `port` - Port selection
189    ///
190    /// # Returns
191    /// A vector of TTL values for each line (0 = inactive, 1 = active).
192    ///
193    /// # Errors
194    /// Returns `NanonisError` if communication fails.
195    pub fn dig_lines_ttl_val_get(&mut self, port: DigitalPort) -> Result<Vec<u32>, NanonisError> {
196        let result = self.quick_send(
197            "DigLines.TTLValGet",
198            vec![NanonisValue::U16(port.into())],
199            vec!["H"],
200            vec!["i", "*I"],
201        )?;
202
203        if result.len() >= 2 {
204            Ok(result[1].as_u32_array()?.to_vec())
205        } else {
206            Err(NanonisError::Protocol("Invalid response".to_string()))
207        }
208    }
209
210    /// Configure and start the pulse generator on the selected digital outputs.
211    ///
212    /// # Arguments
213    /// * `config` - A [`PulseConfig`] struct with pulse configuration
214    ///
215    /// # Errors
216    /// Returns `NanonisError` if communication fails.
217    ///
218    /// # Examples
219    /// ```no_run
220    /// use nanonis_rs::NanonisClient;
221    /// use nanonis_rs::dig_lines::{PulseConfig, DigitalPort};
222    ///
223    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
224    /// let config = PulseConfig {
225    ///     port: DigitalPort::PortA,
226    ///     lines: vec![1, 2],
227    ///     pulse_width_s: 0.001,
228    ///     pulse_pause_s: 0.001,
229    ///     num_pulses: 10,
230    ///     wait_until_finished: true,
231    /// };
232    /// client.dig_lines_pulse(&config)?;
233    /// # Ok::<(), Box<dyn std::error::Error>>(())
234    /// ```
235    pub fn dig_lines_pulse(&mut self, config: &PulseConfig) -> Result<(), NanonisError> {
236        let wait_flag = if config.wait_until_finished {
237            1u32
238        } else {
239            0u32
240        };
241
242        self.quick_send(
243            "DigLines.Pulse",
244            vec![
245                NanonisValue::U16(config.port.into()),
246                NanonisValue::ArrayU8(config.lines.clone()),
247                NanonisValue::F32(config.pulse_width_s),
248                NanonisValue::F32(config.pulse_pause_s),
249                NanonisValue::I32(config.num_pulses),
250                NanonisValue::U32(wait_flag),
251            ],
252            vec!["H", "+*b", "f", "f", "i", "I"],
253            vec![],
254        )?;
255        Ok(())
256    }
257}