nanonis_rs/client/pi_ctrl.rs
1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// PI Controller slope direction.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum PISlope {
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<PISlope> for u16 {
18 fn from(slope: PISlope) -> Self {
19 slope as u16
20 }
21}
22
23impl TryFrom<u16> for PISlope {
24 type Error = NanonisError;
25
26 fn try_from(value: u16) -> Result<Self, Self::Error> {
27 match value {
28 0 => Ok(PISlope::Positive),
29 1 => Ok(PISlope::Negative),
30 _ => Err(NanonisError::Protocol(format!(
31 "Invalid PISlope value: {}",
32 value
33 ))),
34 }
35 }
36}
37
38/// PI Controller properties.
39#[derive(Debug, Clone, Copy, Default)]
40pub struct PICtrlProps {
41 /// Setpoint value
42 pub setpoint: f32,
43 /// Proportional gain
44 pub p_gain: f32,
45 /// Integral gain
46 pub i_gain: f32,
47 /// Slope direction
48 pub slope: PISlope,
49}
50
51/// PI Controller output limits.
52#[derive(Debug, Clone, Copy, Default)]
53pub struct PICtrlLimits {
54 /// Lower output limit
55 pub lower_limit: f32,
56 /// Upper output limit
57 pub upper_limit: f32,
58}
59
60/// Information about available control signals.
61#[derive(Debug, Clone)]
62pub struct ControlSignalInfo {
63 /// Currently selected signal index
64 pub current_index: i32,
65 /// Names of available signals
66 pub signal_names: Vec<String>,
67 /// Indexes of available signals
68 pub signal_indexes: Vec<i32>,
69}
70
71impl NanonisClient {
72 /// Enable or disable a PI controller.
73 ///
74 /// # Arguments
75 /// * `controller_index` - Controller index (1-8)
76 /// * `enabled` - True to enable, false to disable
77 ///
78 /// # Errors
79 /// Returns `NanonisError` if communication fails.
80 ///
81 /// # Examples
82 /// ```no_run
83 /// use nanonis_rs::NanonisClient;
84 ///
85 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
86 /// client.pi_ctrl_on_off_set(1, true)?;
87 /// # Ok::<(), Box<dyn std::error::Error>>(())
88 /// ```
89 pub fn pi_ctrl_on_off_set(
90 &mut self,
91 controller_index: i32,
92 enabled: bool,
93 ) -> Result<(), NanonisError> {
94 let status = if enabled { 1u32 } else { 0u32 };
95 self.quick_send(
96 "PICtrl.OnOffSet",
97 vec![
98 NanonisValue::I32(controller_index),
99 NanonisValue::U32(status),
100 ],
101 vec!["i", "I"],
102 vec![],
103 )?;
104 Ok(())
105 }
106
107 /// Get the on/off status of a PI controller.
108 ///
109 /// # Arguments
110 /// * `controller_index` - Controller index (1-8)
111 ///
112 /// # Returns
113 /// True if controller is enabled.
114 ///
115 /// # Errors
116 /// Returns `NanonisError` if communication fails.
117 pub fn pi_ctrl_on_off_get(&mut self, controller_index: i32) -> Result<bool, NanonisError> {
118 let result = self.quick_send(
119 "PICtrl.OnOffGet",
120 vec![NanonisValue::I32(controller_index)],
121 vec!["i"],
122 vec!["I"],
123 )?;
124
125 if !result.is_empty() {
126 Ok(result[0].as_u32()? != 0)
127 } else {
128 Err(NanonisError::Protocol("Invalid response".to_string()))
129 }
130 }
131
132 /// Set the control channel for a PI controller.
133 ///
134 /// # Arguments
135 /// * `controller_index` - Controller index (1-8)
136 /// * `signal_index` - Control signal index
137 ///
138 /// # Errors
139 /// Returns `NanonisError` if communication fails.
140 pub fn pi_ctrl_ctrl_ch_set(
141 &mut self,
142 controller_index: i32,
143 signal_index: i32,
144 ) -> Result<(), NanonisError> {
145 self.quick_send(
146 "PICtrl.CtrlChSet",
147 vec![
148 NanonisValue::I32(controller_index),
149 NanonisValue::I32(signal_index),
150 ],
151 vec!["i", "i"],
152 vec![],
153 )?;
154 Ok(())
155 }
156
157 /// Get the control channel information for a PI controller.
158 ///
159 /// # Arguments
160 /// * `controller_index` - Controller index (1-8)
161 ///
162 /// # Returns
163 /// A [`ControlSignalInfo`] struct with current and available signals.
164 ///
165 /// # Errors
166 /// Returns `NanonisError` if communication fails.
167 pub fn pi_ctrl_ctrl_ch_get(
168 &mut self,
169 controller_index: i32,
170 ) -> Result<ControlSignalInfo, NanonisError> {
171 let result = self.quick_send(
172 "PICtrl.CtrlChGet",
173 vec![NanonisValue::I32(controller_index)],
174 vec!["i"],
175 vec!["i", "i", "i", "*+c", "i", "*i"],
176 )?;
177
178 if result.len() >= 6 {
179 Ok(ControlSignalInfo {
180 current_index: result[0].as_i32()?,
181 signal_names: result[3].as_string_array()?.to_vec(),
182 signal_indexes: result[5].as_i32_array()?.to_vec(),
183 })
184 } else {
185 Err(NanonisError::Protocol("Invalid response".to_string()))
186 }
187 }
188
189 /// Set the input channel for a PI controller.
190 ///
191 /// # Arguments
192 /// * `controller_index` - Controller index (1-8)
193 /// * `input_index` - Input signal index
194 ///
195 /// # Errors
196 /// Returns `NanonisError` if communication fails.
197 pub fn pi_ctrl_input_ch_set(
198 &mut self,
199 controller_index: i32,
200 input_index: i32,
201 ) -> Result<(), NanonisError> {
202 self.quick_send(
203 "PICtrl.InputChSet",
204 vec![
205 NanonisValue::I32(controller_index),
206 NanonisValue::I32(input_index),
207 ],
208 vec!["i", "i"],
209 vec![],
210 )?;
211 Ok(())
212 }
213
214 /// Get the input channel information for a PI controller.
215 ///
216 /// # Arguments
217 /// * `controller_index` - Controller index (1-8)
218 ///
219 /// # Returns
220 /// A [`ControlSignalInfo`] struct with current and available input signals.
221 ///
222 /// # Errors
223 /// Returns `NanonisError` if communication fails.
224 pub fn pi_ctrl_input_ch_get(
225 &mut self,
226 controller_index: i32,
227 ) -> Result<ControlSignalInfo, NanonisError> {
228 let result = self.quick_send(
229 "PICtrl.InputChGet",
230 vec![NanonisValue::I32(controller_index)],
231 vec!["i"],
232 vec!["i", "i", "i", "*+c", "i", "*i"],
233 )?;
234
235 if result.len() >= 6 {
236 Ok(ControlSignalInfo {
237 current_index: result[0].as_i32()?,
238 signal_names: result[3].as_string_array()?.to_vec(),
239 signal_indexes: result[5].as_i32_array()?.to_vec(),
240 })
241 } else {
242 Err(NanonisError::Protocol("Invalid response".to_string()))
243 }
244 }
245
246 /// Set the properties of a PI controller.
247 ///
248 /// # Arguments
249 /// * `controller_index` - Controller index (1-8)
250 /// * `props` - A [`PICtrlProps`] struct with controller properties
251 ///
252 /// # Errors
253 /// Returns `NanonisError` if communication fails.
254 ///
255 /// # Examples
256 /// ```no_run
257 /// use nanonis_rs::NanonisClient;
258 /// use nanonis_rs::pi_ctrl::{PICtrlProps, PISlope};
259 ///
260 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
261 /// let props = PICtrlProps {
262 /// setpoint: 0.0,
263 /// p_gain: 1.0,
264 /// i_gain: 100.0,
265 /// slope: PISlope::Positive,
266 /// };
267 /// client.pi_ctrl_props_set(1, &props)?;
268 /// # Ok::<(), Box<dyn std::error::Error>>(())
269 /// ```
270 pub fn pi_ctrl_props_set(
271 &mut self,
272 controller_index: i32,
273 props: &PICtrlProps,
274 ) -> Result<(), NanonisError> {
275 self.quick_send(
276 "PICtrl.PropsSet",
277 vec![
278 NanonisValue::I32(controller_index),
279 NanonisValue::F32(props.setpoint),
280 NanonisValue::F32(props.p_gain),
281 NanonisValue::F32(props.i_gain),
282 NanonisValue::U16(props.slope.into()),
283 ],
284 vec!["i", "f", "f", "f", "H"],
285 vec![],
286 )?;
287 Ok(())
288 }
289
290 /// Get the properties of a PI controller.
291 ///
292 /// # Arguments
293 /// * `controller_index` - Controller index (1-8)
294 ///
295 /// # Returns
296 /// A [`PICtrlProps`] struct with current controller properties.
297 ///
298 /// # Errors
299 /// Returns `NanonisError` if communication fails.
300 pub fn pi_ctrl_props_get(&mut self, controller_index: i32) -> Result<PICtrlProps, NanonisError> {
301 let result = self.quick_send(
302 "PICtrl.PropsGet",
303 vec![NanonisValue::I32(controller_index)],
304 vec!["i"],
305 vec!["f", "f", "f", "H"],
306 )?;
307
308 if result.len() >= 4 {
309 Ok(PICtrlProps {
310 setpoint: result[0].as_f32()?,
311 p_gain: result[1].as_f32()?,
312 i_gain: result[2].as_f32()?,
313 slope: result[3].as_u16()?.try_into()?,
314 })
315 } else {
316 Err(NanonisError::Protocol("Invalid response".to_string()))
317 }
318 }
319
320 /// Set the control channel output limits for a PI controller.
321 ///
322 /// # Arguments
323 /// * `controller_index` - Controller index (1-8)
324 /// * `limits` - A [`PICtrlLimits`] struct with output limits
325 ///
326 /// # Errors
327 /// Returns `NanonisError` if communication fails.
328 pub fn pi_ctrl_ctrl_ch_props_set(
329 &mut self,
330 controller_index: i32,
331 limits: &PICtrlLimits,
332 ) -> Result<(), NanonisError> {
333 self.quick_send(
334 "PICtrl.CtrlChPropsSet",
335 vec![
336 NanonisValue::I32(controller_index),
337 NanonisValue::F32(limits.lower_limit),
338 NanonisValue::F32(limits.upper_limit),
339 ],
340 vec!["i", "f", "f"],
341 vec![],
342 )?;
343 Ok(())
344 }
345
346 /// Get the control channel output limits for a PI controller.
347 ///
348 /// # Arguments
349 /// * `controller_index` - Controller index (1-8)
350 ///
351 /// # Returns
352 /// A [`PICtrlLimits`] struct with current output limits.
353 ///
354 /// # Errors
355 /// Returns `NanonisError` if communication fails.
356 pub fn pi_ctrl_ctrl_ch_props_get(
357 &mut self,
358 controller_index: i32,
359 ) -> Result<PICtrlLimits, NanonisError> {
360 let result = self.quick_send(
361 "PICtrl.CtrlChPropsGet",
362 vec![NanonisValue::I32(controller_index)],
363 vec!["i"],
364 vec!["f", "f"],
365 )?;
366
367 if result.len() >= 2 {
368 Ok(PICtrlLimits {
369 lower_limit: result[0].as_f32()?,
370 upper_limit: result[1].as_f32()?,
371 })
372 } else {
373 Err(NanonisError::Protocol("Invalid response".to_string()))
374 }
375 }
376}