Skip to main content

nanonis_rs/client/
data_log.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Data Logger acquisition mode.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum DataLogAcqMode {
8    /// No change to current mode
9    #[default]
10    NoChange = 0,
11    /// Continuous acquisition until stopped
12    Continuous = 1,
13    /// Timed acquisition for set duration
14    Timed = 2,
15}
16
17impl From<DataLogAcqMode> for u16 {
18    fn from(mode: DataLogAcqMode) -> Self {
19        mode as u16
20    }
21}
22
23impl TryFrom<u16> for DataLogAcqMode {
24    type Error = NanonisError;
25
26    fn try_from(value: u16) -> Result<Self, Self::Error> {
27        match value {
28            0 => Ok(DataLogAcqMode::Continuous),
29            1 => Ok(DataLogAcqMode::Timed),
30            _ => Err(NanonisError::Protocol(format!(
31                "Invalid DataLogAcqMode value: {}",
32                value
33            ))),
34        }
35    }
36}
37
38/// Data Logger status information.
39#[derive(Debug, Clone)]
40pub struct DataLogStatus {
41    /// Timestamp when acquisition started
42    pub start_time: String,
43    /// Hours elapsed since acquisition started
44    pub elapsed_hours: u16,
45    /// Minutes displayed
46    pub elapsed_minutes: u16,
47    /// Seconds displayed
48    pub elapsed_seconds: f32,
49    /// Timestamp when acquisition stopped
50    pub stop_time: String,
51    /// Path to last saved file
52    pub saved_file_path: String,
53    /// Number of points saved
54    pub saved_points: i32,
55}
56
57/// Data Logger acquisition configuration.
58#[derive(Debug, Clone)]
59pub struct DataLogProps {
60    /// Acquisition mode
61    pub mode: DataLogAcqMode,
62    /// Acquisition duration hours
63    pub duration_hours: i32,
64    /// Acquisition duration minutes
65    pub duration_minutes: i32,
66    /// Acquisition duration seconds
67    pub duration_seconds: f32,
68    /// Averaging count (samples averaged per data point)
69    pub averaging: i32,
70    /// Base filename for saved files
71    pub basename: String,
72    /// Comment saved in file
73    pub comment: String,
74}
75
76impl Default for DataLogProps {
77    fn default() -> Self {
78        Self {
79            mode: DataLogAcqMode::Continuous,
80            duration_hours: 0,
81            duration_minutes: 0,
82            duration_seconds: 0.0,
83            averaging: 1,
84            basename: String::new(),
85            comment: String::new(),
86        }
87    }
88}
89
90impl NanonisClient {
91    /// Open the Data Logger module.
92    ///
93    /// # Errors
94    /// Returns `NanonisError` if communication fails.
95    ///
96    /// # Examples
97    /// ```no_run
98    /// use nanonis_rs::NanonisClient;
99    ///
100    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
101    /// client.data_log_open()?;
102    /// # Ok::<(), Box<dyn std::error::Error>>(())
103    /// ```
104    pub fn data_log_open(&mut self) -> Result<(), NanonisError> {
105        self.quick_send("DataLog.Open", vec![], vec![], vec![])?;
106        Ok(())
107    }
108
109    /// Start the acquisition in the Data Logger module.
110    ///
111    /// Before using this function, select the channels to record.
112    ///
113    /// # Errors
114    /// Returns `NanonisError` if communication fails.
115    pub fn data_log_start(&mut self) -> Result<(), NanonisError> {
116        self.quick_send("DataLog.Start", vec![], vec![], vec![])?;
117        Ok(())
118    }
119
120    /// Stop the acquisition in the Data Logger module.
121    ///
122    /// # Errors
123    /// Returns `NanonisError` if communication fails.
124    pub fn data_log_stop(&mut self) -> Result<(), NanonisError> {
125        self.quick_send("DataLog.Stop", vec![], vec![], vec![])?;
126        Ok(())
127    }
128
129    /// Get the status of the Data Logger module.
130    ///
131    /// # Returns
132    /// A [`DataLogStatus`] struct with current status information.
133    ///
134    /// # Errors
135    /// Returns `NanonisError` if communication fails.
136    pub fn data_log_status_get(&mut self) -> Result<DataLogStatus, NanonisError> {
137        let result = self.quick_send(
138            "DataLog.StatusGet",
139            vec![],
140            vec![],
141            vec!["i", "*-c", "H", "H", "f", "i", "*-c", "i", "*-c", "i"],
142        )?;
143
144        if result.len() >= 10 {
145            Ok(DataLogStatus {
146                start_time: result[1].as_string()?.to_string(),
147                elapsed_hours: result[2].as_u16()?,
148                elapsed_minutes: result[3].as_u16()?,
149                elapsed_seconds: result[4].as_f32()?,
150                stop_time: result[6].as_string()?.to_string(),
151                saved_file_path: result[8].as_string()?.to_string(),
152                saved_points: result[9].as_i32()?,
153            })
154        } else {
155            Err(NanonisError::Protocol("Invalid response".to_string()))
156        }
157    }
158
159    /// Set the list of recorded channels in the Data Logger.
160    ///
161    /// # Arguments
162    /// * `channel_indexes` - Channel indexes (0-23 for signals in the Signals Manager)
163    ///
164    /// # Errors
165    /// Returns `NanonisError` if communication fails.
166    pub fn data_log_chs_set(&mut self, channel_indexes: &[i32]) -> Result<(), NanonisError> {
167        self.quick_send(
168            "DataLog.ChsSet",
169            vec![NanonisValue::ArrayI32(channel_indexes.to_vec())],
170            vec!["+*i"],
171            vec![],
172        )?;
173        Ok(())
174    }
175
176    /// Get the list of recorded channels in the Data Logger.
177    ///
178    /// # Returns
179    /// A vector of channel indexes.
180    ///
181    /// # Errors
182    /// Returns `NanonisError` if communication fails.
183    pub fn data_log_chs_get(&mut self) -> Result<Vec<i32>, NanonisError> {
184        let result = self.quick_send("DataLog.ChsGet", vec![], vec![], vec!["i", "*i"])?;
185
186        if result.len() >= 2 {
187            Ok(result[1].as_i32_array()?.to_vec())
188        } else {
189            Err(NanonisError::Protocol("Invalid response".to_string()))
190        }
191    }
192
193    /// Set the acquisition configuration for the Data Logger.
194    ///
195    /// # Arguments
196    /// * `props` - A [`DataLogProps`] struct with configuration
197    /// * `modules` - List of module names whose parameters will be saved in file header
198    ///
199    /// # Errors
200    /// Returns `NanonisError` if communication fails.
201    ///
202    /// # Examples
203    /// ```no_run
204    /// use nanonis_rs::NanonisClient;
205    /// use nanonis_rs::data_log::{DataLogProps, DataLogAcqMode};
206    ///
207    /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
208    /// let props = DataLogProps {
209    ///     mode: DataLogAcqMode::Timed,
210    ///     duration_hours: 0,
211    ///     duration_minutes: 10,
212    ///     duration_seconds: 0.0,
213    ///     averaging: 10,
214    ///     basename: "measurement".to_string(),
215    ///     comment: "Test measurement".to_string(),
216    /// };
217    /// client.data_log_props_set(&props, &["Z-Controller".to_string()])?;
218    /// # Ok::<(), Box<dyn std::error::Error>>(())
219    /// ```
220    pub fn data_log_props_set(
221        &mut self,
222        props: &DataLogProps,
223        modules: &[String],
224    ) -> Result<(), NanonisError> {
225        self.quick_send(
226            "DataLog.PropsSet",
227            vec![
228                NanonisValue::U16(props.mode.into()),
229                NanonisValue::I32(props.duration_hours),
230                NanonisValue::I32(props.duration_minutes),
231                NanonisValue::F32(props.duration_seconds),
232                NanonisValue::I32(props.averaging),
233                NanonisValue::String(props.basename.clone()),
234                NanonisValue::String(props.comment.clone()),
235                NanonisValue::ArrayString(modules.to_vec()),
236            ],
237            vec!["H", "i", "i", "f", "i", "+*c", "+*c", "+*c"],
238            vec![],
239        )?;
240        Ok(())
241    }
242
243    /// Get the acquisition configuration for the Data Logger.
244    ///
245    /// # Returns
246    /// A [`DataLogProps`] struct with current configuration.
247    ///
248    /// # Errors
249    /// Returns `NanonisError` if communication fails.
250    pub fn data_log_props_get(&mut self) -> Result<DataLogProps, NanonisError> {
251        let result = self.quick_send(
252            "DataLog.PropsGet",
253            vec![],
254            vec![],
255            vec!["H", "i", "i", "f", "i", "i", "*-c", "i", "*-c"],
256        )?;
257
258        if result.len() >= 9 {
259            Ok(DataLogProps {
260                mode: result[0].as_u16()?.try_into()?,
261                duration_hours: result[1].as_i32()?,
262                duration_minutes: result[2].as_i32()?,
263                duration_seconds: result[3].as_f32()?,
264                averaging: result[4].as_i32()?,
265                basename: result[6].as_string()?.to_string(),
266                comment: result[8].as_string()?.to_string(),
267            })
268        } else {
269            Err(NanonisError::Protocol("Invalid response".to_string()))
270        }
271    }
272}