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