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}