Skip to main content

nanonis_rs/client/
gen_pi_ctrl.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Slope direction for Generic PI controller.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum GenPISlope {
8    /// No change to current setting
9    #[default]
10    NoChange = 0,
11    /// Positive slope
12    Positive = 1,
13    /// Negative slope
14    Negative = 2,
15}
16
17impl From<GenPISlope> for u16 {
18    fn from(slope: GenPISlope) -> Self {
19        slope as u16
20    }
21}
22
23impl TryFrom<u16> for GenPISlope {
24    type Error = NanonisError;
25
26    fn try_from(value: u16) -> Result<Self, Self::Error> {
27        match value {
28            0 => Ok(GenPISlope::Negative),
29            1 => Ok(GenPISlope::Positive),
30            _ => Err(NanonisError::Protocol(format!(
31                "Invalid GenPISlope value: {}",
32                value
33            ))),
34        }
35    }
36}
37
38/// AC mode toggle for demodulator channel.
39#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
40pub enum ACMode {
41    /// No change to current setting
42    #[default]
43    NoChange = 0,
44    /// AC mode on
45    On = 1,
46    /// AC mode off
47    Off = 2,
48}
49
50impl From<ACMode> for u16 {
51    fn from(mode: ACMode) -> Self {
52        mode as u16
53    }
54}
55
56/// Generic PI Controller properties.
57#[derive(Debug, Clone, Copy, Default)]
58pub struct GenPICtrlProps {
59    /// Setpoint value
60    pub setpoint: f32,
61    /// Proportional gain
62    pub p_gain: f32,
63    /// Time constant
64    pub time_constant: f32,
65    /// Slope direction
66    pub slope: GenPISlope,
67}
68
69/// Analog output properties for Generic PI controller.
70#[derive(Debug, Clone)]
71pub struct AOProps {
72    /// Signal name
73    pub signal_name: String,
74    /// Physical units
75    pub units: String,
76    /// Upper physical limit
77    pub upper_limit: f32,
78    /// Lower physical limit
79    pub lower_limit: f32,
80    /// Calibration per volt
81    pub calibration_per_volt: f32,
82    /// Offset in physical units
83    pub offset: f32,
84}
85
86impl Default for AOProps {
87    fn default() -> Self {
88        Self {
89            signal_name: String::new(),
90            units: String::new(),
91            upper_limit: 10.0,
92            lower_limit: -10.0,
93            calibration_per_volt: 1.0,
94            offset: 0.0,
95        }
96    }
97}
98
99impl NanonisClient {
100    /// Enable or disable the Generic PI Controller.
101    ///
102    /// # Arguments
103    /// * `enabled` - True to enable, false to disable
104    ///
105    /// # Errors
106    /// Returns `NanonisError` if communication fails.
107    ///
108    /// # Examples
109    /// ```no_run
110    /// use nanonis_rs::NanonisClient;
111    ///
112    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
113    /// client.gen_pi_ctrl_on_off_set(true)?;
114    /// # Ok::<(), Box<dyn std::error::Error>>(())
115    /// ```
116    pub fn gen_pi_ctrl_on_off_set(&mut self, enabled: bool) -> Result<(), NanonisError> {
117        let status = if enabled { 1u32 } else { 0u32 };
118        self.quick_send(
119            "GenPICtrl.OnOffSet",
120            vec![NanonisValue::U32(status)],
121            vec!["I"],
122            vec![],
123        )?;
124        Ok(())
125    }
126
127    /// Get the on/off status of the Generic PI Controller.
128    ///
129    /// # Returns
130    /// True if controller is enabled.
131    ///
132    /// # Errors
133    /// Returns `NanonisError` if communication fails.
134    pub fn gen_pi_ctrl_on_off_get(&mut self) -> Result<bool, NanonisError> {
135        let result = self.quick_send("GenPICtrl.OnOffGet", vec![], vec![], vec!["I"])?;
136
137        if !result.is_empty() {
138            Ok(result[0].as_u32()? != 0)
139        } else {
140            Err(NanonisError::Protocol("Invalid response".to_string()))
141        }
142    }
143
144    /// Set the output signal value of the User Output controlled by the Generic PI controller.
145    ///
146    /// # Arguments
147    /// * `output_value` - Output value
148    ///
149    /// # Errors
150    /// Returns `NanonisError` if communication fails.
151    pub fn gen_pi_ctrl_ao_val_set(&mut self, output_value: f32) -> Result<(), NanonisError> {
152        self.quick_send(
153            "GenPICtrl.AOValSet",
154            vec![NanonisValue::F32(output_value)],
155            vec!["f"],
156            vec![],
157        )?;
158        Ok(())
159    }
160
161    /// Get the output signal value of the User Output controlled by the Generic PI controller.
162    ///
163    /// # Returns
164    /// The current output value.
165    ///
166    /// # Errors
167    /// Returns `NanonisError` if communication fails.
168    pub fn gen_pi_ctrl_ao_val_get(&mut self) -> Result<f32, NanonisError> {
169        let result = self.quick_send("GenPICtrl.AOValGet", vec![], vec![], vec!["f"])?;
170
171        if !result.is_empty() {
172            Ok(result[0].as_f32()?)
173        } else {
174            Err(NanonisError::Protocol("Invalid response".to_string()))
175        }
176    }
177
178    /// Set the properties of the User Output controlled by the Generic PI controller.
179    ///
180    /// # Arguments
181    /// * `props` - An [`AOProps`] struct with analog output properties
182    ///
183    /// # Errors
184    /// Returns `NanonisError` if communication fails.
185    pub fn gen_pi_ctrl_ao_props_set(&mut self, props: &AOProps) -> Result<(), NanonisError> {
186        self.quick_send(
187            "GenPICtrl.AOPropsSet",
188            vec![
189                NanonisValue::String(props.signal_name.clone()),
190                NanonisValue::String(props.units.clone()),
191                NanonisValue::F32(props.upper_limit),
192                NanonisValue::F32(props.lower_limit),
193                NanonisValue::F32(props.calibration_per_volt),
194                NanonisValue::F32(props.offset),
195            ],
196            vec!["+*c", "+*c", "f", "f", "f", "f"],
197            vec![],
198        )?;
199        Ok(())
200    }
201
202    /// Get the properties of the User Output controlled by the Generic PI controller.
203    ///
204    /// # Returns
205    /// An [`AOProps`] struct with current analog output properties.
206    ///
207    /// # Errors
208    /// Returns `NanonisError` if communication fails.
209    pub fn gen_pi_ctrl_ao_props_get(&mut self) -> Result<AOProps, NanonisError> {
210        let result = self.quick_send(
211            "GenPICtrl.AOPropsGet",
212            vec![],
213            vec![],
214            vec!["i", "*-c", "i", "*-c", "f", "f", "f", "f"],
215        )?;
216
217        if result.len() >= 8 {
218            Ok(AOProps {
219                signal_name: result[1].as_string()?.to_string(),
220                units: result[3].as_string()?.to_string(),
221                upper_limit: result[4].as_f32()?,
222                lower_limit: result[5].as_f32()?,
223                calibration_per_volt: result[6].as_f32()?,
224                offset: result[7].as_f32()?,
225            })
226        } else {
227            Err(NanonisError::Protocol("Invalid response".to_string()))
228        }
229    }
230
231    /// Set the index of the User Output controlled by the Generic PI controller.
232    ///
233    /// # Arguments
234    /// * `output_index` - Output index (1 to number of available outputs)
235    ///
236    /// # Errors
237    /// Returns `NanonisError` if communication fails.
238    pub fn gen_pi_ctrl_mod_ch_set(&mut self, output_index: i32) -> Result<(), NanonisError> {
239        self.quick_send(
240            "GenPICtrl.ModChSet",
241            vec![NanonisValue::I32(output_index)],
242            vec!["i"],
243            vec![],
244        )?;
245        Ok(())
246    }
247
248    /// Get the index of the User Output controlled by the Generic PI controller.
249    ///
250    /// # Returns
251    /// The output index (0 means no output selected).
252    ///
253    /// # Errors
254    /// Returns `NanonisError` if communication fails.
255    pub fn gen_pi_ctrl_mod_ch_get(&mut self) -> Result<i32, NanonisError> {
256        let result = self.quick_send("GenPICtrl.ModChGet", vec![], vec![], vec!["i"])?;
257
258        if !result.is_empty() {
259            Ok(result[0].as_i32()?)
260        } else {
261            Err(NanonisError::Protocol("Invalid response".to_string()))
262        }
263    }
264
265    /// Set the index of the signal demodulated by the Generic PI controller.
266    ///
267    /// # Arguments
268    /// * `input_index` - Input index (0-127 for signals, -1 for no change)
269    /// * `ac_mode` - AC mode setting
270    ///
271    /// # Errors
272    /// Returns `NanonisError` if communication fails.
273    pub fn gen_pi_ctrl_demod_ch_set(
274        &mut self,
275        input_index: i32,
276        ac_mode: ACMode,
277    ) -> Result<(), NanonisError> {
278        self.quick_send(
279            "GenPICtrl.DemodChSet",
280            vec![
281                NanonisValue::I32(input_index),
282                NanonisValue::U16(ac_mode.into()),
283            ],
284            vec!["i", "H"],
285            vec![],
286        )?;
287        Ok(())
288    }
289
290    /// Get the index of the signal demodulated by the Generic PI controller.
291    ///
292    /// # Returns
293    /// The input index.
294    ///
295    /// # Errors
296    /// Returns `NanonisError` if communication fails.
297    pub fn gen_pi_ctrl_demod_ch_get(&mut self) -> Result<i32, NanonisError> {
298        let result = self.quick_send("GenPICtrl.DemodChGet", vec![], vec![], vec!["i"])?;
299
300        if !result.is_empty() {
301            Ok(result[0].as_i32()?)
302        } else {
303            Err(NanonisError::Protocol("Invalid response".to_string()))
304        }
305    }
306
307    /// Set the properties of the Generic PI controller.
308    ///
309    /// # Arguments
310    /// * `props` - A [`GenPICtrlProps`] struct with controller properties
311    ///
312    /// # Errors
313    /// Returns `NanonisError` if communication fails.
314    ///
315    /// # Examples
316    /// ```no_run
317    /// use nanonis_rs::NanonisClient;
318    /// use nanonis_rs::gen_pi_ctrl::{GenPICtrlProps, GenPISlope};
319    ///
320    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
321    /// let props = GenPICtrlProps {
322    ///     setpoint: 0.0,
323    ///     p_gain: 1.0,
324    ///     time_constant: 0.001,
325    ///     slope: GenPISlope::Positive,
326    /// };
327    /// client.gen_pi_ctrl_props_set(&props)?;
328    /// # Ok::<(), Box<dyn std::error::Error>>(())
329    /// ```
330    pub fn gen_pi_ctrl_props_set(&mut self, props: &GenPICtrlProps) -> Result<(), NanonisError> {
331        self.quick_send(
332            "GenPICtrl.PropsSet",
333            vec![
334                NanonisValue::F32(props.setpoint),
335                NanonisValue::F32(props.p_gain),
336                NanonisValue::F32(props.time_constant),
337                NanonisValue::U16(props.slope.into()),
338            ],
339            vec!["f", "f", "f", "H"],
340            vec![],
341        )?;
342        Ok(())
343    }
344
345    /// Get the properties of the Generic PI controller.
346    ///
347    /// # Returns
348    /// A [`GenPICtrlProps`] struct with current controller properties.
349    ///
350    /// # Errors
351    /// Returns `NanonisError` if communication fails.
352    pub fn gen_pi_ctrl_props_get(&mut self) -> Result<GenPICtrlProps, NanonisError> {
353        let result =
354            self.quick_send("GenPICtrl.PropsGet", vec![], vec![], vec!["f", "f", "f", "H"])?;
355
356        if result.len() >= 4 {
357            Ok(GenPICtrlProps {
358                setpoint: result[0].as_f32()?,
359                p_gain: result[1].as_f32()?,
360                time_constant: result[2].as_f32()?,
361                slope: result[3].as_u16()?.try_into()?,
362            })
363        } else {
364            Err(NanonisError::Protocol("Invalid response".to_string()))
365        }
366    }
367}