Skip to main content

nanonis_rs/client/
util.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Version information returned by the Nanonis software.
6///
7/// Contains detailed version and release information about both the
8/// host application and RT Engine components of the Nanonis system.
9#[derive(Debug, Clone, PartialEq)]
10pub struct VersionInfo {
11    /// Product line name (e.g., "Nanonis SPM Control Software" or "Nanonis Tramea Software")
12    pub product_line: String,
13    /// Software version string (e.g., "Generic 5")
14    pub version: String,
15    /// Host application release number
16    pub host_app_release: u32,
17    /// RT Engine application release number
18    pub rt_engine_release: u32,
19}
20
21impl NanonisClient {
22    /// Get the session path from the Nanonis software.
23    ///
24    /// Returns the current session path where Nanonis stores configuration
25    /// and data files for the active session.
26    ///
27    /// # Returns
28    /// The session path as a String.
29    ///
30    /// # Errors
31    /// Returns `NanonisError` if communication fails or protocol error occurs.
32    ///
33    /// # Examples
34    /// ```no_run
35    /// use nanonis_rs::NanonisClient;
36    ///
37    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
38    ///
39    /// let session_path = client.util_session_path_get()?;
40    /// println!("Session path: {}", session_path);
41    /// # Ok::<(), Box<dyn std::error::Error>>(())
42    /// ```
43    pub fn util_session_path_get(&mut self) -> Result<String, NanonisError> {
44        let result = self.quick_send("Util.SessionPathGet", vec![], vec![], vec!["i", "*-c"])?;
45
46        match result.get(1) {
47            Some(value) => Ok(value.as_string()?.to_string()),
48            None => Err(NanonisError::Protocol(
49                "No session path returned".to_string(),
50            )),
51        }
52    }
53
54    /// Load settings from a specified .ini file.
55    ///
56    /// Loads the Nanonis configuration settings from the specified file path.
57    /// Can also automatically load settings from the session file.
58    ///
59    /// # Arguments
60    /// * `settings_file_path` - Path to the settings file to load
61    /// * `load_session_settings` - If true, automatically loads from session file (bypasses path argument)
62    ///
63    /// # Errors
64    /// Returns `NanonisError` if the file doesn't exist, can't be read, or communication fails.
65    ///
66    /// # Examples
67    /// ```no_run
68    /// use nanonis_rs::NanonisClient;
69    ///
70    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
71    ///
72    /// // Load from specific file
73    /// client.util_settings_load("/path/to/settings.ini", false)?;
74    ///
75    /// // Or load from session file
76    /// client.util_settings_load("", true)?;
77    /// # Ok::<(), Box<dyn std::error::Error>>(())
78    /// ```
79    pub fn util_settings_load(
80        &mut self,
81        settings_file_path: &str,
82        load_session_settings: bool,
83    ) -> Result<(), NanonisError> {
84        self.quick_send(
85            "Util.SettingsLoad",
86            vec![
87                NanonisValue::String(settings_file_path.to_string()),
88                NanonisValue::U32(if load_session_settings { 1 } else { 0 }),
89            ],
90            vec!["+*c", "I"],
91            vec![],
92        )?;
93        Ok(())
94    }
95
96    /// Save current settings to a specified .ini file.
97    ///
98    /// Saves the current Nanonis configuration settings to the specified file path.
99    /// Can also automatically save to the session file.
100    ///
101    /// # Arguments
102    /// * `settings_file_path` - Path where the settings file will be saved
103    /// * `save_session_settings` - If true, automatically saves to session file (bypasses path argument)
104    ///
105    /// # Errors
106    /// Returns `NanonisError` if the file can't be written or 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    ///
114    /// // Save to specific file
115    /// client.util_settings_save("/path/to/settings.ini", false)?;
116    ///
117    /// // Or save to session file
118    /// client.util_settings_save("", true)?;
119    /// # Ok::<(), Box<dyn std::error::Error>>(())
120    /// ```
121    pub fn util_settings_save(
122        &mut self,
123        settings_file_path: &str,
124        save_session_settings: bool,
125    ) -> Result<(), NanonisError> {
126        self.quick_send(
127            "Util.SettingsSave",
128            vec![
129                NanonisValue::String(settings_file_path.to_string()),
130                NanonisValue::U32(if save_session_settings { 1 } else { 0 }),
131            ],
132            vec!["+*c", "I"],
133            vec![],
134        )?;
135        Ok(())
136    }
137
138    /// Load a layout from a specified .ini file.
139    ///
140    /// Loads the Nanonis UI layout configuration from the specified file path.
141    /// Can also automatically load the layout from the session file.
142    ///
143    /// # Arguments
144    /// * `layout_file_path` - Path to the layout file to load
145    /// * `load_session_layout` - If true, automatically loads from session file (bypasses path argument)
146    ///
147    /// # Errors
148    /// Returns `NanonisError` if the file doesn't exist, can't be read, or communication fails.
149    ///
150    /// # Examples
151    /// ```no_run
152    /// use nanonis_rs::NanonisClient;
153    ///
154    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
155    ///
156    /// // Load from specific file
157    /// client.util_layout_load("/path/to/layout.ini", false)?;
158    ///
159    /// // Or load from session file
160    /// client.util_layout_load("", true)?;
161    /// # Ok::<(), Box<dyn std::error::Error>>(())
162    /// ```
163    pub fn util_layout_load(
164        &mut self,
165        layout_file_path: &str,
166        load_session_layout: bool,
167    ) -> Result<(), NanonisError> {
168        self.quick_send(
169            "Util.LayoutLoad",
170            vec![
171                NanonisValue::String(layout_file_path.to_string()),
172                NanonisValue::U32(if load_session_layout { 1 } else { 0 }),
173            ],
174            vec!["+*c", "I"],
175            vec![],
176        )?;
177        Ok(())
178    }
179
180    /// Save current layout to a specified .ini file.
181    ///
182    /// Saves the current Nanonis UI layout configuration to the specified file path.
183    /// Can also automatically save to the session file.
184    ///
185    /// # Arguments
186    /// * `layout_file_path` - Path where the layout file will be saved
187    /// * `save_session_layout` - If true, automatically saves to session file (bypasses path argument)
188    ///
189    /// # Errors
190    /// Returns `NanonisError` if the file can't be written or communication fails.
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 to specific file
199    /// client.util_layout_save("/path/to/layout.ini", false)?;
200    ///
201    /// // Or save to session file
202    /// client.util_layout_save("", true)?;
203    /// # Ok::<(), Box<dyn std::error::Error>>(())
204    /// ```
205    pub fn util_layout_save(
206        &mut self,
207        layout_file_path: &str,
208        save_session_layout: bool,
209    ) -> Result<(), NanonisError> {
210        self.quick_send(
211            "Util.LayoutSave",
212            vec![
213                NanonisValue::String(layout_file_path.to_string()),
214                NanonisValue::U32(if save_session_layout { 1 } else { 0 }),
215            ],
216            vec!["+*c", "I"],
217            vec![],
218        )?;
219        Ok(())
220    }
221
222    /// Lock the Nanonis software interface.
223    ///
224    /// Launches a lock modal window that prevents user interaction with the
225    /// Nanonis software until it is unlocked manually or through `util_unlock()`.
226    /// This is useful for automated experiments where you want to prevent
227    /// accidental user interference.
228    ///
229    /// # Errors
230    /// Returns `NanonisError` if communication fails or protocol error occurs.
231    ///
232    /// # Examples
233    /// ```no_run
234    /// use nanonis_rs::NanonisClient;
235    /// use std::thread;
236    /// use std::time::Duration;
237    ///
238    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
239    ///
240    /// // Lock the interface during automated experiment
241    /// client.util_lock()?;
242    ///
243    /// // Perform automated operations
244    /// thread::sleep(Duration::from_secs(5));
245    ///
246    /// // Unlock when done
247    /// client.util_unlock()?;
248    /// # Ok::<(), Box<dyn std::error::Error>>(())
249    /// ```
250    pub fn util_lock(&mut self) -> Result<(), NanonisError> {
251        self.quick_send("Util.Lock", vec![], vec![], vec![])?;
252        Ok(())
253    }
254
255    /// Unlock the Nanonis software interface.
256    ///
257    /// Closes the lock modal window that prevents user interaction with the
258    /// Nanonis software. Use this after `util_lock()` when automated operations
259    /// are complete.
260    ///
261    /// # Errors
262    /// Returns `NanonisError` if communication fails or protocol error occurs.
263    ///
264    /// # Examples
265    /// ```no_run
266    /// use nanonis_rs::NanonisClient;
267    ///
268    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
269    ///
270    /// // Unlock the interface
271    /// client.util_unlock()?;
272    /// # Ok::<(), Box<dyn std::error::Error>>(())
273    /// ```
274    pub fn util_unlock(&mut self) -> Result<(), NanonisError> {
275        self.quick_send("Util.UnLock", vec![], vec![], vec![])?;
276        Ok(())
277    }
278
279    /// Set the Real Time controller frequency.
280    ///
281    /// Configures the frequency of the Real Time (RT) controller which determines
282    /// the speed at which the feedback loop and other real-time operations run.
283    ///
284    /// # Arguments
285    /// * `rt_frequency` - RT frequency in Hz
286    ///
287    /// # Errors
288    /// Returns `NanonisError` if invalid frequency provided or communication fails.
289    ///
290    /// # Examples
291    /// ```no_run
292    /// use nanonis_rs::NanonisClient;
293    ///
294    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
295    ///
296    /// // Set RT frequency to 20 kHz
297    /// client.util_rt_freq_set(20000.0)?;
298    /// # Ok::<(), Box<dyn std::error::Error>>(())
299    /// ```
300    pub fn util_rt_freq_set(&mut self, rt_frequency: f32) -> Result<(), NanonisError> {
301        self.quick_send(
302            "Util.RTFreqSet",
303            vec![NanonisValue::F32(rt_frequency)],
304            vec!["f"],
305            vec![],
306        )?;
307        Ok(())
308    }
309
310    /// Get the Real Time controller frequency.
311    ///
312    /// Returns the current frequency of the Real Time (RT) controller.
313    ///
314    /// # Returns
315    /// RT frequency in Hz.
316    ///
317    /// # Errors
318    /// Returns `NanonisError` if communication fails or protocol error occurs.
319    ///
320    /// # Examples
321    /// ```no_run
322    /// use nanonis_rs::NanonisClient;
323    ///
324    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
325    ///
326    /// let frequency = client.util_rt_freq_get()?;
327    /// println!("RT frequency: {} Hz", frequency);
328    /// # Ok::<(), Box<dyn std::error::Error>>(())
329    /// ```
330    pub fn util_rt_freq_get(&mut self) -> Result<f32, NanonisError> {
331        let result = self.quick_send("Util.RTFreqGet", vec![], vec![], vec!["f"])?;
332
333        match result.first() {
334            Some(value) => Ok(value.as_f32()?),
335            None => Err(NanonisError::Protocol(
336                "No RT frequency returned".to_string(),
337            )),
338        }
339    }
340
341    /// Set the Acquisition Period in the TCP Receiver.
342    ///
343    /// Configures the period at which data is acquired and transmitted
344    /// via the TCP interface.
345    ///
346    /// # Arguments
347    /// * `acquisition_period` - Acquisition period in seconds
348    ///
349    /// # Errors
350    /// Returns `NanonisError` if invalid period provided or communication fails.
351    ///
352    /// # Examples
353    /// ```no_run
354    /// use nanonis_rs::NanonisClient;
355    ///
356    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
357    ///
358    /// // Set acquisition period to 100 ms
359    /// client.util_acq_period_set(0.1)?;
360    /// # Ok::<(), Box<dyn std::error::Error>>(())
361    /// ```
362    pub fn util_acq_period_set(&mut self, acquisition_period: f32) -> Result<(), NanonisError> {
363        self.quick_send(
364            "Util.AcqPeriodSet",
365            vec![NanonisValue::F32(acquisition_period)],
366            vec!["f"],
367            vec![],
368        )?;
369        Ok(())
370    }
371
372    /// Get the Acquisition Period from the TCP Receiver.
373    ///
374    /// Returns the current acquisition period configured in the TCP Receiver.
375    ///
376    /// # Returns
377    /// Acquisition period in seconds.
378    ///
379    /// # Errors
380    /// Returns `NanonisError` if communication fails or protocol error occurs.
381    ///
382    /// # Examples
383    /// ```no_run
384    /// use nanonis_rs::NanonisClient;
385    ///
386    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
387    ///
388    /// let period = client.util_acq_period_get()?;
389    /// println!("Acquisition period: {} s ({} ms)", period, period * 1000.0);
390    /// # Ok::<(), Box<dyn std::error::Error>>(())
391    /// ```
392    pub fn util_acq_period_get(&mut self) -> Result<f32, NanonisError> {
393        let result = self.quick_send("Util.AcqPeriodGet", vec![], vec![], vec!["f"])?;
394
395        match result.first() {
396            Some(value) => Ok(value.as_f32()?),
397            None => Err(NanonisError::Protocol(
398                "No acquisition period returned".to_string(),
399            )),
400        }
401    }
402
403    /// Set the Real-time oversampling in the TCP Receiver.
404    ///
405    /// Configures the oversampling factor for the 24 signals on the RT engine
406    /// before they are sent to the host. The oversampling affects the maximum
407    /// Spectrum Analyzer frequency and other displays.
408    ///
409    /// # Arguments
410    /// * `rt_oversampling` - RT oversampling factor
411    ///
412    /// # Errors
413    /// Returns `NanonisError` if invalid oversampling value or communication fails.
414    ///
415    /// # Examples
416    /// ```no_run
417    /// use nanonis_rs::NanonisClient;
418    ///
419    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
420    ///
421    /// // Set oversampling to 10x
422    /// client.util_rt_oversampl_set(10)?;
423    /// # Ok::<(), Box<dyn std::error::Error>>(())
424    /// ```
425    pub fn util_rt_oversampl_set(&mut self, rt_oversampling: i32) -> Result<(), NanonisError> {
426        self.quick_send(
427            "Util.RTOversamplSet",
428            vec![NanonisValue::I32(rt_oversampling)],
429            vec!["i"],
430            vec![],
431        )?;
432        Ok(())
433    }
434
435    /// Get the Real-time oversampling from the TCP Receiver.
436    ///
437    /// Returns the current oversampling factor configured for RT engine signals.
438    ///
439    /// # Returns
440    /// RT oversampling factor.
441    ///
442    /// # Errors
443    /// Returns `NanonisError` if communication fails or protocol error occurs.
444    ///
445    /// # Examples
446    /// ```no_run
447    /// use nanonis_rs::NanonisClient;
448    ///
449    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
450    ///
451    /// let oversampling = client.util_rt_oversampl_get()?;
452    /// println!("RT oversampling: {}x", oversampling);
453    /// # Ok::<(), Box<dyn std::error::Error>>(())
454    /// ```
455    pub fn util_rt_oversampl_get(&mut self) -> Result<i32, NanonisError> {
456        let result = self.quick_send("Util.RTOversamplGet", vec![], vec![], vec!["i"])?;
457
458        match result.first() {
459            Some(value) => Ok(value.as_i32()?),
460            None => Err(NanonisError::Protocol(
461                "No RT oversampling returned".to_string(),
462            )),
463        }
464    }
465
466    /// Quit the Nanonis software with options to save settings, layout, and signals.
467    ///
468    /// Provides the same functionality as the dialog that appears when quitting
469    /// through the File menu. Can save settings, layout, and signal configurations
470    /// before exiting.
471    ///
472    /// **Warning**: This will close the Nanonis software and terminate the TCP connection.
473    ///
474    /// # Arguments
475    /// * `use_stored_values` - If true, uses stored quit preferences (ignores other arguments)
476    /// * `settings_name` - Name of settings file to save (empty string = don't save)
477    /// * `layout_name` - Name of layout file to save (empty string = don't save)
478    /// * `save_signals` - If true, saves signal configuration
479    ///
480    /// # Errors
481    /// Returns `NanonisError` if communication fails before quit completes.
482    ///
483    /// # Examples
484    /// ```no_run
485    /// use nanonis_rs::NanonisClient;
486    ///
487    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
488    ///
489    /// // Quit without saving anything
490    /// client.util_quit(false, "", "", false)?;
491    ///
492    /// // Quit and save everything with stored preferences
493    /// // client.util_quit(true, "", "", false)?;
494    ///
495    /// // Quit and save specific settings and layout
496    /// // client.util_quit(false, "my_settings", "my_layout", true)?;
497    /// # Ok::<(), Box<dyn std::error::Error>>(())
498    /// ```
499    pub fn util_quit(
500        &mut self,
501        use_stored_values: bool,
502        settings_name: &str,
503        layout_name: &str,
504        save_signals: bool,
505    ) -> Result<(), NanonisError> {
506        self.quick_send(
507            "Util.Quit",
508            vec![
509                NanonisValue::U32(if use_stored_values { 1 } else { 0 }),
510                NanonisValue::String(settings_name.to_string()),
511                NanonisValue::String(layout_name.to_string()),
512                NanonisValue::U32(if save_signals { 1 } else { 0 }),
513            ],
514            vec!["I", "+*c", "+*c", "I"],
515            vec![],
516        )?;
517        Ok(())
518    }
519
520    /// Get version information from the Nanonis software.
521    ///
522    /// Returns detailed version information about the Nanonis system including
523    /// product line, version string, and release numbers for both host application
524    /// and RT Engine.
525    ///
526    /// # Returns
527    /// A `VersionInfo` struct containing all version details.
528    ///
529    /// # Errors
530    /// Returns `NanonisError` if communication fails or protocol error occurs.
531    ///
532    /// # Examples
533    /// ```no_run
534    /// use nanonis_rs::NanonisClient;
535    ///
536    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
537    ///
538    /// let version = client.util_version_get()?;
539    /// println!("Product: {}", version.product_line);
540    /// println!("Version: {}", version.version);
541    /// println!("Host release: {}", version.host_app_release);
542    /// println!("RT Engine release: {}", version.rt_engine_release);
543    /// # Ok::<(), Box<dyn std::error::Error>>(())
544    /// ```
545    pub fn util_version_get(&mut self) -> Result<VersionInfo, NanonisError> {
546        let result = self.quick_send(
547            "Util.VersionGet",
548            vec![],
549            vec![],
550            vec!["+*c", "+*c", "I", "I"],
551        )?;
552
553        if result.len() >= 4 {
554            Ok(VersionInfo {
555                product_line: result[0].as_string()?.to_string(),
556                version: result[1].as_string()?.to_string(),
557                host_app_release: result[2].as_u32()?,
558                rt_engine_release: result[3].as_u32()?,
559            })
560        } else {
561            Err(NanonisError::Protocol(
562                "Invalid version info response".to_string(),
563            ))
564        }
565    }
566}