nanonis_rs/client/signals/mod.rs
1mod types;
2pub use types::*;
3
4use super::NanonisClient;
5use crate::error::NanonisError;
6use crate::types::NanonisValue;
7
8impl NanonisClient {
9 /// Get available signal names
10 pub fn signal_names_get(&mut self) -> Result<Vec<String>, NanonisError> {
11 let result = self.quick_send("Signals.NamesGet", vec![], vec![], vec!["+*c"])?;
12 match result.first() {
13 Some(value) => {
14 let signal_names = value.as_string_array()?.to_vec();
15
16 Ok(signal_names)
17 }
18 None => Err(NanonisError::Protocol(
19 "No signal names returned".to_string(),
20 )),
21 }
22 }
23
24 /// Get calibration and offset of a signal by index
25 pub fn signals_calibr_get(
26 &mut self,
27 signal_index: SignalIndex,
28 ) -> Result<(f32, f32), NanonisError> {
29 let result = self.quick_send(
30 "Signals.CalibrGet",
31 vec![NanonisValue::I32(signal_index.into())],
32 vec!["i"],
33 vec!["f", "f"],
34 )?;
35 if result.len() >= 2 {
36 Ok((result[0].as_f32()?, result[1].as_f32()?))
37 } else {
38 Err(NanonisError::Protocol(
39 "Invalid calibration response".to_string(),
40 ))
41 }
42 }
43
44 /// Get range limits of a signal by index
45 pub fn signals_range_get(
46 &mut self,
47 signal_index: SignalIndex,
48 ) -> Result<(f32, f32), NanonisError> {
49 let result = self.quick_send(
50 "Signals.RangeGet",
51 vec![NanonisValue::I32(signal_index.into())],
52 vec!["i"],
53 vec!["f", "f"],
54 )?;
55 if result.len() >= 2 {
56 Ok((result[0].as_f32()?, result[1].as_f32()?)) // (max, min)
57 } else {
58 Err(NanonisError::Protocol("Invalid range response".to_string()))
59 }
60 }
61
62 /// Get current values of multiple signals by index.
63 ///
64 /// Accepts raw `i32` indices for backwards compatibility. Prefer
65 /// [`signals_vals_get_by_idx`](Self::signals_vals_get_by_idx) for a
66 /// type-safe interface using `SignalIndex`.
67 pub fn signals_vals_get(
68 &mut self,
69 signal_indexes: Vec<i32>,
70 wait_for_newest_data: bool,
71 ) -> Result<Vec<f32>, NanonisError> {
72 let wait_flag = if wait_for_newest_data { 1u32 } else { 0u32 };
73
74 let result = self.quick_send(
75 "Signals.ValsGet",
76 vec![
77 NanonisValue::ArrayI32(signal_indexes),
78 NanonisValue::U32(wait_flag),
79 ],
80 vec!["+*i", "I"],
81 vec!["i", "*f"],
82 )?;
83
84 if result.len() >= 2 {
85 match result.into_iter().nth(1) {
86 Some(NanonisValue::ArrayF32(values)) => Ok(values),
87 _ => Err(NanonisError::Protocol(
88 "Invalid signal values response".to_string(),
89 )),
90 }
91 } else {
92 Err(NanonisError::Protocol(
93 "Incomplete signal values response".to_string(),
94 ))
95 }
96 }
97
98 /// Get current values of multiple signals using typed `SignalIndex`.
99 ///
100 /// # Examples
101 /// ```no_run
102 /// use nanonis_rs::NanonisClient;
103 /// use nanonis_rs::signals::SignalIndex;
104 ///
105 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
106 /// let indices = [SignalIndex::new(0), SignalIndex::new(1), SignalIndex::new(76)];
107 /// let values = client.signals_vals_get_by_idx(&indices, true)?;
108 /// # Ok::<(), Box<dyn std::error::Error>>(())
109 /// ```
110 pub fn signals_vals_get_by_idx(
111 &mut self,
112 signal_indexes: &[SignalIndex],
113 wait_for_newest_data: bool,
114 ) -> Result<Vec<f32>, NanonisError> {
115 let indexes: Vec<i32> = signal_indexes.iter().map(|s| i32::from(*s)).collect();
116 self.signals_vals_get(indexes, wait_for_newest_data)
117 }
118
119 /// Get the current value of a single selected signal.
120 ///
121 /// Returns the current value of the selected signal, oversampled during the
122 /// Acquisition Period time (Tap). The signal is continuously oversampled and published
123 /// every Tap seconds.
124 ///
125 /// # Signal Measurement Principle
126 /// This function waits for the next oversampled data to be published and returns its value.
127 /// It does not trigger a measurement but waits for data to be published. The function
128 /// returns a value 0 to Tap seconds after being called.
129 ///
130 /// **Important**: If you change a signal and immediately call this function, you might
131 /// get "old" data measured before the signal change. Set `wait_for_newest_data` to `true`
132 /// to ensure you get only fresh data.
133 ///
134 /// # Arguments
135 /// * `signal_index` - Signal index (0-127)
136 /// * `wait_for_newest_data` - If `true`, discards first value and waits for fresh data.
137 /// Takes Tap to 2*Tap seconds. If `false`, returns next available value (0 to Tap seconds).
138 ///
139 /// # Returns
140 /// The signal value in physical units.
141 ///
142 /// # Errors
143 /// Returns `NanonisError` if:
144 /// - Invalid signal index provided
145 /// - Communication timeout or protocol error
146 ///
147 /// # Examples
148 /// ```no_run
149 /// use nanonis_rs::NanonisClient;
150 /// use nanonis_rs::signals::SignalIndex;
151 ///
152 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
153 ///
154 /// // Read bias signal immediately
155 /// let bias_value = client.signal_val_get(SignalIndex(24), false)?;
156 ///
157 /// // Wait for fresh data after signal change
158 /// let fresh_value = client.signal_val_get(SignalIndex(24), true)?;
159 /// # Ok::<(), Box<dyn std::error::Error>>(())
160 /// ```
161 pub fn signal_val_get(
162 &mut self,
163 signal_index: impl Into<SignalIndex>,
164 wait_for_newest_data: bool,
165 ) -> Result<f32, NanonisError> {
166 let wait_flag = if wait_for_newest_data { 1u32 } else { 0u32 };
167
168 let result = self.quick_send(
169 "Signals.ValGet",
170 vec![
171 NanonisValue::I32(signal_index.into().into()),
172 NanonisValue::U32(wait_flag),
173 ],
174 vec!["i", "I"],
175 vec!["f"],
176 )?;
177
178 match result.first() {
179 Some(value) => Ok(value.as_f32()?),
180 None => Err(NanonisError::Protocol(
181 "No signal value returned".to_string(),
182 )),
183 }
184 }
185
186 /// Get the list of measurement channels names available in the software.
187 ///
188 /// Returns the names of measurement channels used in sweepers and other measurement modules.
189 ///
190 /// **Important Note**: Measurement channels are different from Signals. Measurement channels
191 /// are used in sweepers, while Signals are used by graphs and other modules. The indexes
192 /// returned here are used for sweeper channel configuration (e.g., `GenSwp.ChannelsGet/Set`).
193 ///
194 /// # Returns
195 /// A vector of measurement channel names where each name corresponds to an index
196 /// that can be used in sweeper functions.
197 ///
198 /// # Errors
199 /// Returns `NanonisError` if communication fails or protocol error occurs.
200 ///
201 /// # Examples
202 /// ```no_run
203 /// use nanonis_rs::NanonisClient;
204 ///
205 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
206 ///
207 /// let meas_channels = client.signals_meas_names_get()?;
208 /// println!("Available measurement channels: {}", meas_channels.len());
209 ///
210 /// for (index, name) in meas_channels.iter().enumerate() {
211 /// println!(" {}: {}", index, name);
212 /// }
213 /// # Ok::<(), Box<dyn std::error::Error>>(())
214 /// ```
215 pub fn signals_meas_names_get(&mut self) -> Result<Vec<String>, NanonisError> {
216 let result = self.quick_send(
217 "Signals.MeasNamesGet",
218 vec![],
219 vec![],
220 vec!["i", "i", "*+c"],
221 )?;
222
223 if result.len() >= 3 {
224 let meas_names = result[2].as_string_array()?.to_vec();
225 Ok(meas_names)
226 } else {
227 Err(NanonisError::Protocol(
228 "Invalid measurement names response".to_string(),
229 ))
230 }
231 }
232
233 /// Get the list of additional Real-Time (RT) signals and current assignments.
234 ///
235 /// Returns the list of additional RT signals available for assignment to Internal 23 and 24,
236 /// plus the names of signals currently assigned to these internal channels.
237 ///
238 /// **Note**: This assignment in the Signals Manager doesn't automatically make them available
239 /// in graphs and modules. Internal 23 and 24 must be assigned to one of the 24 display slots
240 /// using functions like `Signals.InSlotSet` to be visible in the software.
241 ///
242 /// # Returns
243 /// A tuple containing:
244 /// - `Vec<String>` - List of additional RT signals that can be assigned to Internal 23/24
245 /// - `String` - Name of RT signal currently assigned to Internal 23
246 /// - `String` - Name of RT signal currently assigned to Internal 24
247 ///
248 /// # Errors
249 /// Returns `NanonisError` if communication fails or protocol error occurs.
250 ///
251 /// # Examples
252 /// ```no_run
253 /// use nanonis_rs::NanonisClient;
254 ///
255 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
256 ///
257 /// let (available_signals, internal_23, internal_24) = client.signals_add_rt_get()?;
258 ///
259 /// println!("Available additional RT signals: {}", available_signals.len());
260 /// for (i, signal) in available_signals.iter().enumerate() {
261 /// println!(" {}: {}", i, signal);
262 /// }
263 ///
264 /// println!("Internal 23 assigned to: {}", internal_23);
265 /// println!("Internal 24 assigned to: {}", internal_24);
266 /// # Ok::<(), Box<dyn std::error::Error>>(())
267 /// ```
268 pub fn signals_add_rt_get(&mut self) -> Result<(Vec<String>, String, String), NanonisError> {
269 let result = self.quick_send(
270 "Signals.AddRTGet",
271 vec![],
272 vec![],
273 vec!["i", "i", "*+c", "i", "*-c", "i", "*-c"],
274 )?;
275
276 if result.len() >= 7 {
277 let available_signals = result[2].as_string_array()?.to_vec();
278 let internal_23 = result[4].as_string()?.to_string();
279 let internal_24 = result[6].as_string()?.to_string();
280 Ok((available_signals, internal_23, internal_24))
281 } else {
282 Err(NanonisError::Protocol(
283 "Invalid additional RT signals response".to_string(),
284 ))
285 }
286 }
287
288 /// Assign additional Real-Time (RT) signals to Internal 23 and 24 signals.
289 ///
290 /// Links advanced RT signals to Internal 23 and Internal 24 in the Signals Manager.
291 /// This enables routing of specialized real-time signals through the internal channel system.
292 ///
293 /// **Important Note**: This assignment only links the RT signals to Internal 23/24.
294 /// To make them visible in graphs and available for acquisition in modules, Internal 23 and 24
295 /// must be assigned to one of the 24 display slots using functions like `Signals.InSlotSet`.
296 ///
297 /// # Arguments
298 /// * `additional_rt_signal_1` - Index of the RT signal to assign to Internal 23 (from `signals_add_rt_get()`)
299 /// * `additional_rt_signal_2` - Index of the RT signal to assign to Internal 24 (from `signals_add_rt_get()`)
300 ///
301 /// # Errors
302 /// Returns `NanonisError` if:
303 /// - Invalid RT signal indices provided
304 /// - RT signals are not available or accessible
305 /// - Communication fails or protocol error occurs
306 ///
307 /// # Examples
308 /// ```no_run
309 /// use nanonis_rs::NanonisClient;
310 ///
311 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
312 ///
313 /// // First, get the available RT signals
314 /// let (available_signals, current_23, current_24) = client.signals_add_rt_get()?;
315 ///
316 /// println!("Available RT signals:");
317 /// for (i, signal) in available_signals.iter().enumerate() {
318 /// println!(" {}: {}", i, signal);
319 /// }
320 ///
321 /// // Assign RT signal index 0 to Internal 23 and index 1 to Internal 24
322 /// client.signals_add_rt_set(0, 1)?;
323 ///
324 /// // Verify the assignment
325 /// let (_, new_23, new_24) = client.signals_add_rt_get()?;
326 /// println!("Internal 23 now assigned to: {}", new_23);
327 /// println!("Internal 24 now assigned to: {}", new_24);
328 /// # Ok::<(), Box<dyn std::error::Error>>(())
329 /// ```
330 pub fn signals_add_rt_set(
331 &mut self,
332 additional_rt_signal_1: i32,
333 additional_rt_signal_2: i32,
334 ) -> Result<(), NanonisError> {
335 self.quick_send(
336 "Signals.AddRTSet",
337 vec![
338 NanonisValue::I32(additional_rt_signal_1),
339 NanonisValue::I32(additional_rt_signal_2),
340 ],
341 vec!["i", "i"],
342 vec![],
343 )?;
344 Ok(())
345 }
346
347 /// Get the input slot assignments for each signal.
348 ///
349 /// Returns the hardware input slot index for each signal in the system.
350 /// Input slots represent the physical or virtual input sources that feed
351 /// into each signal channel.
352 ///
353 /// # Returns
354 /// Vector of input slot indices, one for each signal in the system.
355 ///
356 /// # Errors
357 /// Returns `NanonisError` if communication fails or protocol error occurs.
358 ///
359 /// # Examples
360 /// ```no_run
361 /// use nanonis_rs::NanonisClient;
362 ///
363 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
364 ///
365 /// let input_slots = client.signals_in_slots_get()?;
366 /// println!("Input slot assignments for {} signals:", input_slots.len());
367 /// for (signal_idx, slot_idx) in input_slots.iter().enumerate() {
368 /// println!(" Signal {}: Input slot {}", signal_idx, slot_idx);
369 /// }
370 /// # Ok::<(), Box<dyn std::error::Error>>(())
371 /// ```
372 pub fn signals_in_slots_get(&mut self) -> Result<Vec<i32>, NanonisError> {
373 let result = self.quick_send("Signals.InSlotsGet", vec![], vec![], vec!["i", "*i"])?;
374
375 if result.len() >= 2 {
376 Ok(result[1].as_i32_array()?.to_vec())
377 } else {
378 Err(NanonisError::Protocol(
379 "Invalid input slots response".to_string(),
380 ))
381 }
382 }
383}