Skip to main content

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}