nanonis_rs/client/tip_recovery.rs
1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4use std::time::Duration;
5
6/// Configuration parameters for tip shaper
7#[derive(Debug, Clone)]
8pub struct TipShaperConfig {
9 pub switch_off_delay: Duration,
10 pub change_bias: bool,
11 pub bias_v: f32,
12 pub tip_lift_m: f32,
13 pub lift_time_1: Duration,
14 pub bias_lift_v: f32,
15 pub bias_settling_time: Duration,
16 pub lift_height_m: f32,
17 pub lift_time_2: Duration,
18 pub end_wait_time: Duration,
19 pub restore_feedback: bool,
20}
21
22/// Return type for tip shaper properties
23pub type TipShaperProps = (f32, u32, f32, f32, f32, f32, f32, f32, f32, f32, u32);
24
25impl NanonisClient {
26 /// Set the buffer size of the Tip Move Recorder.
27 ///
28 /// Sets the number of data elements that can be stored in the Tip Move Recorder
29 /// buffer. This recorder tracks signal values while the tip is moving in Follow Me mode.
30 /// **Note**: This function clears the existing graph data.
31 ///
32 /// # Arguments
33 /// * `buffer_size` - Number of data elements to store in the recorder buffer
34 ///
35 /// # Errors
36 /// Returns `NanonisError` if communication fails or invalid buffer size.
37 ///
38 /// # Examples
39 /// ```no_run
40 /// use nanonis_rs::NanonisClient;
41 ///
42 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
43 ///
44 /// // Set buffer for 10,000 data points
45 /// client.tip_rec_buffer_size_set(10000)?;
46 ///
47 /// // Set smaller buffer for quick tests
48 /// client.tip_rec_buffer_size_set(1000)?;
49 /// # Ok::<(), Box<dyn std::error::Error>>(())
50 /// ```
51 pub fn tip_rec_buffer_size_set(&mut self, buffer_size: i32) -> Result<(), NanonisError> {
52 self.quick_send(
53 "TipRec.BufferSizeSet",
54 vec![NanonisValue::I32(buffer_size)],
55 vec!["i"],
56 vec![],
57 )?;
58 Ok(())
59 }
60
61 /// Get the current buffer size of the Tip Move Recorder.
62 ///
63 /// Returns the number of data elements that can be stored in the recorder buffer.
64 ///
65 /// # Returns
66 /// Current buffer size (number of data elements).
67 ///
68 /// # Errors
69 /// Returns `NanonisError` if communication fails.
70 ///
71 /// # Examples
72 /// ```no_run
73 /// use nanonis_rs::NanonisClient;
74 ///
75 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
76 ///
77 /// let buffer_size = client.tip_rec_buffer_size_get()?;
78 /// println!("Tip recorder buffer size: {} points", buffer_size);
79 /// # Ok::<(), Box<dyn std::error::Error>>(())
80 /// ```
81 pub fn tip_rec_buffer_size_get(&mut self) -> Result<i32, NanonisError> {
82 let result = self.quick_send("TipRec.BufferSizeGet", vec![], vec![], vec!["i"])?;
83
84 match result.first() {
85 Some(value) => Ok(value.as_i32()?),
86 None => Err(NanonisError::Protocol(
87 "No buffer size returned".to_string(),
88 )),
89 }
90 }
91
92 /// Clear the buffer of the Tip Move Recorder.
93 ///
94 /// Removes all recorded data from the Tip Move Recorder buffer, resetting
95 /// it to an empty state. This is useful before starting a new recording session.
96 ///
97 /// # Errors
98 /// Returns `NanonisError` if communication fails.
99 ///
100 /// # Examples
101 /// ```no_run
102 /// use nanonis_rs::NanonisClient;
103 ///
104 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
105 ///
106 /// // Clear buffer before starting new measurement
107 /// client.tip_rec_buffer_clear()?;
108 /// println!("Tip recorder buffer cleared");
109 /// # Ok::<(), Box<dyn std::error::Error>>(())
110 /// ```
111 pub fn tip_rec_buffer_clear(&mut self) -> Result<(), NanonisError> {
112 self.quick_send("TipRec.BufferClear", vec![], vec![], vec![])?;
113 Ok(())
114 }
115
116 /// Get the recorded data from the Tip Move Recorder.
117 ///
118 /// Returns all data recorded while the tip was moving in Follow Me mode.
119 /// This includes channel indexes, names, and the complete 2D data array
120 /// with measurements taken during tip movement.
121 ///
122 /// # Returns
123 /// A tuple containing:
124 /// - `Vec<i32>` - Channel indexes (0-23 for Signals Manager slots)
125 /// - `Vec<Vec<f32>>` - 2D data array \[rows\]\[columns\] with recorded measurements
126 ///
127 /// # Errors
128 /// Returns `NanonisError` if communication fails or no data available.
129 ///
130 /// # Examples
131 /// ```no_run
132 /// use nanonis_rs::NanonisClient;
133 ///
134 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
135 ///
136 /// // Get recorded tip movement data
137 /// let (channel_indexes, data) = client.tip_rec_data_get()?;
138 ///
139 /// println!("Recorded {} channels with {} data points",
140 /// channel_indexes.len(), data.len());
141 ///
142 /// // Analyze data for each channel
143 /// for (i, &channel_idx) in channel_indexes.iter().enumerate() {
144 /// if i < data[0].len() {
145 /// println!("Channel {}: {} values", channel_idx, data.len());
146 /// }
147 /// }
148 /// # Ok::<(), Box<dyn std::error::Error>>(())
149 /// ```
150 pub fn tip_rec_data_get(&mut self) -> Result<(Vec<i32>, Vec<Vec<f32>>), NanonisError> {
151 let result = self.quick_send(
152 "TipRec.DataGet",
153 vec![],
154 vec![],
155 vec!["i", "*i", "i", "i", "2f"],
156 )?;
157
158 if result.len() >= 5 {
159 let channel_indexes = result[1].as_i32_array()?.to_vec();
160 let rows = result[2].as_i32()? as usize;
161 let cols = result[3].as_i32()? as usize;
162
163 // Parse 2D data array
164 let flat_data = result[4].as_f32_array()?;
165 let mut data_2d = Vec::with_capacity(rows);
166 for row in 0..rows {
167 let start_idx = row * cols;
168 let end_idx = start_idx + cols;
169 data_2d.push(flat_data[start_idx..end_idx].to_vec());
170 }
171
172 Ok((channel_indexes, data_2d))
173 } else {
174 Err(NanonisError::Protocol(
175 "Invalid tip recorder data response".to_string(),
176 ))
177 }
178 }
179
180 /// Save the tip movement data to a file.
181 ///
182 /// Saves all data recorded in Follow Me mode to a file with the specified basename.
183 /// Optionally clears the buffer after saving to prepare for new recordings.
184 ///
185 /// # Arguments
186 /// * `clear_buffer` - If `true`, clears buffer after saving
187 /// * `basename` - Base filename for saved data (empty to use last basename)
188 ///
189 /// # Errors
190 /// Returns `NanonisError` if communication fails or file save error occurs.
191 ///
192 /// # Examples
193 /// ```no_run
194 /// use nanonis_rs::NanonisClient;
195 ///
196 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
197 ///
198 /// // Save data and clear buffer for next measurement
199 /// client.tip_rec_data_save(true, "tip_approach_001")?;
200 ///
201 /// // Save without clearing buffer (keep data for analysis)
202 /// client.tip_rec_data_save(false, "tip_movement_log")?;
203 /// # Ok::<(), Box<dyn std::error::Error>>(())
204 /// ```
205 pub fn tip_rec_data_save(
206 &mut self,
207 clear_buffer: bool,
208 basename: &str,
209 ) -> Result<(), NanonisError> {
210 let clear_flag = if clear_buffer { 1u32 } else { 0u32 };
211
212 self.quick_send(
213 "TipRec.DataSave",
214 vec![
215 NanonisValue::U32(clear_flag),
216 NanonisValue::String(basename.to_string()),
217 ],
218 vec!["I", "+*c"],
219 vec![],
220 )?;
221 Ok(())
222 }
223
224 /// Start the tip shaper procedure for tip conditioning.
225 ///
226 /// Initiates the tip shaper procedure which performs controlled tip conditioning
227 /// by applying specific voltage sequences and mechanical movements. This is used
228 /// to improve tip sharpness and stability after crashes or contamination.
229 ///
230 /// # Arguments
231 /// * `wait_until_finished` - If `true`, waits for procedure completion
232 /// * `timeout_ms` - Timeout in milliseconds (-1 for infinite wait)
233 ///
234 /// # Errors
235 /// Returns `NanonisError` if communication fails or procedure cannot start.
236 ///
237 /// # Examples
238 /// ```no_run
239 /// use nanonis_rs::NanonisClient;
240 /// use std::time::Duration;
241 ///
242 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
243 ///
244 /// // Start tip shaping and wait for completion (30 second timeout)
245 /// client.tip_shaper_start(true, Duration::from_secs(30))?;
246 /// println!("Tip shaping completed");
247 ///
248 /// // Start tip shaping without waiting
249 /// client.tip_shaper_start(false, Duration::ZERO)?;
250 /// # Ok::<(), Box<dyn std::error::Error>>(())
251 /// ```
252 pub fn tip_shaper_start(
253 &mut self,
254 wait_until_finished: bool,
255 timeout: Duration,
256 ) -> Result<(), NanonisError> {
257 let wait_flag = if wait_until_finished { 1u32 } else { 0u32 };
258 let timeout = timeout.as_millis().min(u32::MAX as u128) as i32;
259
260 self.quick_send(
261 "TipShaper.Start",
262 vec![NanonisValue::U32(wait_flag), NanonisValue::I32(timeout)],
263 vec!["I", "i"],
264 vec![],
265 )?;
266 Ok(())
267 }
268
269 /// Set the tip shaper procedure configuration.
270 ///
271 /// Configures all parameters for the tip conditioning procedure including
272 /// timing, voltages, and mechanical movements. This is a complex procedure
273 /// with multiple stages of tip treatment.
274 ///
275 /// # Arguments
276 /// * `config` - Tip shaper configuration parameters
277 ///
278 /// # Errors
279 /// Returns `NanonisError` if communication fails or invalid parameters.
280 ///
281 /// # Examples
282 /// ```no_run
283 /// use nanonis_rs::NanonisClient;
284 /// use nanonis_rs::tip_recovery::TipShaperConfig;
285 /// use std::time::Duration;
286 ///
287 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
288 ///
289 /// // Conservative tip conditioning parameters
290 /// let config = TipShaperConfig {
291 /// switch_off_delay: Duration::from_millis(100),
292 /// change_bias: true,
293 /// bias_v: -2.0,
294 /// tip_lift_m: 50e-9, // 50 nm
295 /// lift_time_1: Duration::from_secs(1),
296 /// bias_lift_v: 5.0,
297 /// bias_settling_time: Duration::from_millis(500),
298 /// lift_height_m: 100e-9, // 100 nm
299 /// lift_time_2: Duration::from_millis(500),
300 /// end_wait_time: Duration::from_millis(200),
301 /// restore_feedback: true,
302 /// };
303 /// client.tip_shaper_props_set(config)?;
304 /// # Ok::<(), Box<dyn std::error::Error>>(())
305 /// ```
306 pub fn tip_shaper_props_set(&mut self, config: TipShaperConfig) -> Result<(), NanonisError> {
307 self.quick_send(
308 "TipShaper.PropsSet",
309 vec![
310 NanonisValue::F32(config.switch_off_delay.as_secs_f32()),
311 NanonisValue::U32(config.change_bias.into()),
312 NanonisValue::F32(config.bias_v),
313 NanonisValue::F32(config.tip_lift_m),
314 NanonisValue::F32(config.lift_time_1.as_secs_f32()),
315 NanonisValue::F32(config.bias_lift_v),
316 NanonisValue::F32(config.bias_settling_time.as_secs_f32()),
317 NanonisValue::F32(config.lift_height_m),
318 NanonisValue::F32(config.lift_time_2.as_secs_f32()),
319 NanonisValue::F32(config.end_wait_time.as_secs_f32()),
320 NanonisValue::U32(config.restore_feedback.into()),
321 ],
322 vec!["f", "I", "f", "f", "f", "f", "f", "f", "f", "f", "I"],
323 vec![],
324 )?;
325 Ok(())
326 }
327
328 /// Get the current tip shaper procedure configuration.
329 ///
330 /// Returns all parameters currently configured for the tip conditioning procedure.
331 /// Use this to verify settings before starting the procedure.
332 ///
333 /// # Returns
334 /// A tuple containing all tip shaper parameters:
335 /// - `f32` - Switch off delay (s)
336 /// - `u32` - Change bias flag (0=no change, 1=true, 2=false)
337 /// - `f32` - Bias voltage (V)
338 /// - `f32` - Tip lift distance (m)
339 /// - `f32` - First lift time (s)
340 /// - `f32` - Bias lift voltage (V)
341 /// - `f32` - Bias settling time (s)
342 /// - `f32` - Second lift height (m)
343 /// - `f32` - Second lift time (s)
344 /// - `f32` - End wait time (s)
345 /// - `u32` - Restore feedback flag (0=no change, 1=true, 2=false)
346 ///
347 /// # Errors
348 /// Returns `NanonisError` if communication fails.
349 ///
350 /// # Examples
351 /// ```no_run
352 /// use nanonis_rs::NanonisClient;
353 ///
354 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
355 ///
356 /// let (switch_delay, change_bias, bias_v, tip_lift, lift_time1,
357 /// bias_lift, settling, lift_height, lift_time2, end_wait, restore) =
358 /// client.tip_shaper_props_get()?;
359 ///
360 /// println!("Tip lift: {:.1} nm, Bias: {:.1} V", tip_lift * 1e9, bias_v);
361 /// println!("Total procedure time: ~{:.1} s",
362 /// switch_delay + lift_time1 + settling + lift_time2 + end_wait);
363 /// # Ok::<(), Box<dyn std::error::Error>>(())
364 /// ```
365 pub fn tip_shaper_props_get(&mut self) -> Result<TipShaperProps, NanonisError> {
366 let result = self.quick_send(
367 "TipShaper.PropsGet",
368 vec![],
369 vec![],
370 vec!["f", "I", "f", "f", "f", "f", "f", "f", "f", "f", "I"],
371 )?;
372
373 if result.len() >= 11 {
374 Ok((
375 result[0].as_f32()?, // switch_off_delay
376 result[1].as_u32()?, // change_bias
377 result[2].as_f32()?, // bias_v
378 result[3].as_f32()?, // tip_lift_m
379 result[4].as_f32()?, // lift_time_1_s
380 result[5].as_f32()?, // bias_lift_v
381 result[6].as_f32()?, // bias_settling_time_s
382 result[7].as_f32()?, // lift_height_m
383 result[8].as_f32()?, // lift_time_2_s
384 result[9].as_f32()?, // end_wait_time_s
385 result[10].as_u32()?, // restore_feedback
386 ))
387 } else {
388 Err(NanonisError::Protocol(
389 "Invalid tip shaper properties response".to_string(),
390 ))
391 }
392 }
393
394 /// Get the Tip Shaper properties as a type-safe TipShaperConfig struct
395 ///
396 /// This method returns the same information as `tip_shaper_props_get()` but
397 /// with Duration types for time fields instead of raw f32 seconds.
398 ///
399 /// # Returns
400 /// Returns a `TipShaperConfig` struct with type-safe Duration fields for all time parameters.
401 ///
402 /// # Errors
403 /// Returns `NanonisError` if communication fails or if the response format is invalid.
404 ///
405 /// # Examples
406 /// ```no_run
407 /// use nanonis_rs::NanonisClient;
408 /// use std::time::Duration;
409 ///
410 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
411 /// let config = client.tip_shaper_config_get()?;
412 ///
413 /// println!("Tip lift: {:.1} nm, Bias: {:.1} V",
414 /// config.tip_lift_m * 1e9, config.bias_v);
415 /// println!("Total procedure time: {:.1} s",
416 /// (config.switch_off_delay + config.lift_time_1 +
417 /// config.bias_settling_time + config.lift_time_2 +
418 /// config.end_wait_time).as_secs_f32());
419 /// # Ok::<(), Box<dyn std::error::Error>>(())
420 /// ```
421 pub fn tip_shaper_config_get(&mut self) -> Result<TipShaperConfig, NanonisError> {
422 let result = self.quick_send(
423 "TipShaper.PropsGet",
424 vec![],
425 vec![],
426 vec!["f", "I", "f", "f", "f", "f", "f", "f", "f", "f", "I"],
427 )?;
428
429 if result.len() >= 11 {
430 let restore_feedback = match result[10].as_u32()? {
431 0 => true,
432 1 => false,
433 _ => panic!("Wrong return value for restore_feedback"),
434 };
435
436 let change_bias = match result[1].as_u32()? {
437 0 => true,
438 1 => false,
439 _ => panic!("Wrong return value for change_bias"),
440 };
441
442 Ok(TipShaperConfig {
443 switch_off_delay: Duration::from_secs_f32(result[0].as_f32()?),
444 change_bias,
445 bias_v: result[2].as_f32()?,
446 tip_lift_m: result[3].as_f32()?,
447 lift_time_1: Duration::from_secs_f32(result[4].as_f32()?),
448 bias_lift_v: result[5].as_f32()?,
449 bias_settling_time: Duration::from_secs_f32(result[6].as_f32()?),
450 lift_height_m: result[7].as_f32()?,
451 lift_time_2: Duration::from_secs_f32(result[8].as_f32()?),
452 end_wait_time: Duration::from_secs_f32(result[9].as_f32()?),
453 restore_feedback,
454 })
455 } else {
456 Err(NanonisError::Protocol(
457 "Invalid tip shaper properties response".to_string(),
458 ))
459 }
460 }
461}