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}