Skip to main content

nanonis_rs/client/
hs_swp.rs

1use super::NanonisClient;
2use crate::error::NanonisError;
3use crate::types::NanonisValue;
4
5/// Comparison condition for auto-reverse.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
7pub enum ReverseCondition {
8    /// Signal greater than threshold
9    #[default]
10    GreaterThan = 0,
11    /// Signal less than threshold
12    LessThan = 1,
13}
14
15impl From<ReverseCondition> for i32 {
16    fn from(c: ReverseCondition) -> Self {
17        c as i32
18    }
19}
20
21impl TryFrom<i32> for ReverseCondition {
22    type Error = NanonisError;
23
24    fn try_from(value: i32) -> Result<Self, Self::Error> {
25        match value {
26            0 => Ok(ReverseCondition::GreaterThan),
27            1 => Ok(ReverseCondition::LessThan),
28            _ => Err(NanonisError::Protocol(format!(
29                "Invalid ReverseCondition value: {}",
30                value
31            ))),
32        }
33    }
34}
35
36/// Linkage mode for second auto-reverse condition.
37#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
38pub enum ConditionLinkage {
39    /// No second condition
40    #[default]
41    Off = 0,
42    /// Either condition 1 or 2 must be met
43    Or = 1,
44    /// Both conditions must be met simultaneously
45    And = 2,
46    /// Condition 1 must be met first, then condition 2
47    Then = 3,
48}
49
50impl From<ConditionLinkage> for i32 {
51    fn from(l: ConditionLinkage) -> Self {
52        l as i32
53    }
54}
55
56impl TryFrom<i32> for ConditionLinkage {
57    type Error = NanonisError;
58
59    fn try_from(value: i32) -> Result<Self, Self::Error> {
60        match value {
61            0 => Ok(ConditionLinkage::Off),
62            1 => Ok(ConditionLinkage::Or),
63            2 => Ok(ConditionLinkage::And),
64            3 => Ok(ConditionLinkage::Then),
65            _ => Err(NanonisError::Protocol(format!(
66                "Invalid ConditionLinkage value: {}",
67                value
68            ))),
69        }
70    }
71}
72
73/// Auto-reverse configuration for high-speed sweeper.
74#[derive(Debug, Clone, Copy, Default)]
75pub struct HSSwpAutoReverse {
76    /// Enable auto-reverse
77    pub enabled: bool,
78    /// First condition comparison type
79    pub condition: ReverseCondition,
80    /// First condition signal index
81    pub signal_index: i32,
82    /// First condition threshold
83    pub threshold: f32,
84    /// Linkage to second condition
85    pub linkage: ConditionLinkage,
86    /// Second condition comparison type
87    pub condition2: ReverseCondition,
88    /// Second condition signal index
89    pub signal2_index: i32,
90    /// Second condition threshold
91    pub threshold2: f32,
92}
93
94/// Sweep timing parameters.
95#[derive(Debug, Clone, Copy, Default)]
96pub struct HSSwpTiming {
97    /// Initial settling time in seconds
98    pub initial_settling_s: f32,
99    /// Settling time in seconds
100    pub settling_s: f32,
101    /// Integration time in seconds
102    pub integration_s: f32,
103    /// Maximum slew rate in units/s
104    pub max_slew_rate: f32,
105}
106
107/// Sweep channel limits configuration.
108#[derive(Debug, Clone, Copy, Default)]
109pub struct HSSwpLimits {
110    /// Use relative limits (vs absolute)
111    pub relative: bool,
112    /// Start value
113    pub start: f32,
114    /// Stop value
115    pub stop: f32,
116}
117
118/// Z-controller behavior during sweep.
119#[derive(Debug, Clone, Copy, Default)]
120pub struct HSSwpZCtrl {
121    /// Whether to switch off Z-controller during sweep
122    pub switch_off: bool,
123    /// Z-controller index (1 = tip 1, 2-4 for multiprobe)
124    pub controller_index: i32,
125    /// Time to average Z position before switch-off
126    pub averaging_time_s: f32,
127    /// Z offset for tip retraction
128    pub z_offset_m: f32,
129    /// Time to wait after switching back on
130    pub control_time_s: f32,
131}
132
133/// Available channels information.
134#[derive(Debug, Clone, Default)]
135pub struct HSSwpAvailableChannels {
136    /// Currently selected channel indices
137    pub selected_indices: Vec<u32>,
138    /// Names of available channels
139    pub available_names: Vec<String>,
140    /// Indices of available channels
141    pub available_indices: Vec<i32>,
142}
143
144/// Sweep signal information.
145#[derive(Debug, Clone, Default)]
146pub struct HSSwpSignalList {
147    /// Signal names
148    pub names: Vec<String>,
149    /// Signal indices
150    pub indices: Vec<i32>,
151}
152
153/// Save options configuration.
154#[derive(Debug, Clone, Default)]
155pub struct HSSwpSaveOptions {
156    /// Comment for file header
157    pub comment: String,
158    /// Module names for parameters to save
159    pub modules: Vec<String>,
160}
161
162impl NanonisClient {
163    /// Set the acquisition channels for the high-speed sweeper.
164    ///
165    /// # Arguments
166    /// * `channel_indices` - Indices of channels to record
167    ///
168    /// # Errors
169    /// Returns `NanonisError` if communication fails.
170    pub fn hs_swp_acq_chs_set(&mut self, channel_indices: &[i32]) -> Result<(), NanonisError> {
171        self.quick_send(
172            "HSSwp.AcqChsSet",
173            vec![NanonisValue::ArrayI32(channel_indices.to_vec())],
174            vec!["+*i"],
175            vec![],
176        )?;
177        Ok(())
178    }
179
180    /// Get the acquisition channels for the high-speed sweeper.
181    ///
182    /// # Returns
183    /// An [`HSSwpAvailableChannels`] struct with channel information.
184    ///
185    /// # Errors
186    /// Returns `NanonisError` if communication fails.
187    pub fn hs_swp_acq_chs_get(&mut self) -> Result<HSSwpAvailableChannels, NanonisError> {
188        let result = self.quick_send(
189            "HSSwp.AcqChsGet",
190            vec![],
191            vec![],
192            vec!["i", "*I", "i", "i", "*+c", "i", "*i"],
193        )?;
194
195        if result.len() >= 7 {
196            Ok(HSSwpAvailableChannels {
197                selected_indices: result[1].as_u32_array()?.to_vec(),
198                available_names: result[4].as_string_array()?.to_vec(),
199                available_indices: result[6].as_i32_array()?.to_vec(),
200            })
201        } else {
202            Err(NanonisError::Protocol("Invalid response".to_string()))
203        }
204    }
205
206    /// Set the auto-reverse configuration.
207    ///
208    /// # Arguments
209    /// * `config` - Auto-reverse configuration
210    ///
211    /// # Errors
212    /// Returns `NanonisError` if communication fails.
213    pub fn hs_swp_auto_reverse_set(
214        &mut self,
215        config: &HSSwpAutoReverse,
216    ) -> Result<(), NanonisError> {
217        let on_off = if config.enabled { 1i32 } else { 0i32 };
218        self.quick_send(
219            "HSSwp.AutoReverseSet",
220            vec![
221                NanonisValue::I32(on_off),
222                NanonisValue::I32(config.condition.into()),
223                NanonisValue::I32(config.signal_index),
224                NanonisValue::F32(config.threshold),
225                NanonisValue::I32(config.linkage.into()),
226                NanonisValue::I32(config.condition2.into()),
227                NanonisValue::I32(config.signal2_index),
228                NanonisValue::F32(config.threshold2),
229            ],
230            vec!["i", "i", "i", "f", "i", "i", "i", "f"],
231            vec![],
232        )?;
233        Ok(())
234    }
235
236    /// Get the auto-reverse configuration.
237    ///
238    /// # Returns
239    /// An [`HSSwpAutoReverse`] struct with current configuration.
240    ///
241    /// # Errors
242    /// Returns `NanonisError` if communication fails.
243    pub fn hs_swp_auto_reverse_get(&mut self) -> Result<HSSwpAutoReverse, NanonisError> {
244        let result = self.quick_send(
245            "HSSwp.AutoReverseGet",
246            vec![],
247            vec![],
248            vec!["i", "i", "i", "f", "i", "i", "i", "f"],
249        )?;
250
251        if result.len() >= 8 {
252            Ok(HSSwpAutoReverse {
253                enabled: result[0].as_i32()? != 0,
254                condition: result[1].as_i32()?.try_into()?,
255                signal_index: result[2].as_i32()?,
256                threshold: result[3].as_f32()?,
257                linkage: result[4].as_i32()?.try_into()?,
258                condition2: result[5].as_i32()?.try_into()?,
259                signal2_index: result[6].as_i32()?,
260                threshold2: result[7].as_f32()?,
261            })
262        } else {
263            Err(NanonisError::Protocol("Invalid response".to_string()))
264        }
265    }
266
267    /// Set the end settling time.
268    ///
269    /// # Arguments
270    /// * `time_s` - End settling time in seconds
271    ///
272    /// # Errors
273    /// Returns `NanonisError` if communication fails.
274    pub fn hs_swp_end_settl_set(&mut self, time_s: f32) -> Result<(), NanonisError> {
275        self.quick_send(
276            "HSSwp.EndSettlSet",
277            vec![NanonisValue::F32(time_s)],
278            vec!["f"],
279            vec![],
280        )?;
281        Ok(())
282    }
283
284    /// Get the end settling time.
285    ///
286    /// # Returns
287    /// End settling time in seconds.
288    ///
289    /// # Errors
290    /// Returns `NanonisError` if communication fails.
291    pub fn hs_swp_end_settl_get(&mut self) -> Result<f32, NanonisError> {
292        let result = self.quick_send("HSSwp.EndSettlGet", vec![], vec![], vec!["f"])?;
293
294        if !result.is_empty() {
295            Ok(result[0].as_f32()?)
296        } else {
297            Err(NanonisError::Protocol("Invalid response".to_string()))
298        }
299    }
300
301    /// Set the number of sweeps.
302    ///
303    /// # Arguments
304    /// * `num_sweeps` - Number of sweeps (ignored if continuous)
305    /// * `continuous` - Enable continuous sweep mode
306    ///
307    /// # Errors
308    /// Returns `NanonisError` if communication fails.
309    pub fn hs_swp_num_sweeps_set(
310        &mut self,
311        num_sweeps: u32,
312        continuous: bool,
313    ) -> Result<(), NanonisError> {
314        let cont_flag = if continuous { 1i32 } else { 0i32 };
315        self.quick_send(
316            "HSSwp.NumSweepsSet",
317            vec![NanonisValue::U32(num_sweeps), NanonisValue::I32(cont_flag)],
318            vec!["I", "i"],
319            vec![],
320        )?;
321        Ok(())
322    }
323
324    /// Get the number of sweeps.
325    ///
326    /// # Returns
327    /// Tuple of (number of sweeps, continuous mode enabled).
328    ///
329    /// # Errors
330    /// Returns `NanonisError` if communication fails.
331    pub fn hs_swp_num_sweeps_get(&mut self) -> Result<(u32, bool), NanonisError> {
332        let result = self.quick_send("HSSwp.NumSweepsGet", vec![], vec![], vec!["I", "i"])?;
333
334        if result.len() >= 2 {
335            Ok((result[0].as_u32()?, result[1].as_i32()? != 0))
336        } else {
337            Err(NanonisError::Protocol("Invalid response".to_string()))
338        }
339    }
340
341    /// Set whether signals are reset at sweep end.
342    ///
343    /// # Arguments
344    /// * `reset` - True to reset signals at sweep end
345    ///
346    /// # Errors
347    /// Returns `NanonisError` if communication fails.
348    pub fn hs_swp_reset_signals_set(&mut self, reset: bool) -> Result<(), NanonisError> {
349        let flag = if reset { 1i32 } else { 0i32 };
350        self.quick_send(
351            "HSSwp.ResetSignalsSet",
352            vec![NanonisValue::I32(flag)],
353            vec!["i"],
354            vec![],
355        )?;
356        Ok(())
357    }
358
359    /// Get whether signals are reset at sweep end.
360    ///
361    /// # Returns
362    /// True if signals are reset at sweep end.
363    ///
364    /// # Errors
365    /// Returns `NanonisError` if communication fails.
366    pub fn hs_swp_reset_signals_get(&mut self) -> Result<bool, NanonisError> {
367        let result = self.quick_send("HSSwp.ResetSignalsGet", vec![], vec![], vec!["i"])?;
368
369        if !result.is_empty() {
370            Ok(result[0].as_i32()? != 0)
371        } else {
372            Err(NanonisError::Protocol("Invalid response".to_string()))
373        }
374    }
375
376    /// Set the save basename and path.
377    ///
378    /// # Arguments
379    /// * `basename` - Base name for saved files
380    /// * `path` - Directory path for saved files
381    ///
382    /// # Errors
383    /// Returns `NanonisError` if communication fails.
384    pub fn hs_swp_save_basename_set(
385        &mut self,
386        basename: &str,
387        path: &str,
388    ) -> Result<(), NanonisError> {
389        self.quick_send(
390            "HSSwp.SaveBasenameSet",
391            vec![
392                NanonisValue::String(basename.to_string()),
393                NanonisValue::String(path.to_string()),
394            ],
395            vec!["+*c", "+*c"],
396            vec![],
397        )?;
398        Ok(())
399    }
400
401    /// Get the save basename and path.
402    ///
403    /// # Returns
404    /// Tuple of (basename, path).
405    ///
406    /// # Errors
407    /// Returns `NanonisError` if communication fails.
408    pub fn hs_swp_save_basename_get(&mut self) -> Result<(String, String), NanonisError> {
409        let result = self.quick_send(
410            "HSSwp.SaveBasenameGet",
411            vec![],
412            vec![],
413            vec!["i", "*-c", "*-c"],
414        )?;
415
416        if result.len() >= 3 {
417            Ok((
418                result[1].as_string()?.to_string(),
419                result[2].as_string()?.to_string(),
420            ))
421        } else {
422            Err(NanonisError::Protocol("Invalid response".to_string()))
423        }
424    }
425
426    /// Set whether data is saved.
427    ///
428    /// # Arguments
429    /// * `save` - True to save data
430    ///
431    /// # Errors
432    /// Returns `NanonisError` if communication fails.
433    pub fn hs_swp_save_data_set(&mut self, save: bool) -> Result<(), NanonisError> {
434        let flag = if save { 1i32 } else { 0i32 };
435        self.quick_send(
436            "HSSwp.SaveDataSet",
437            vec![NanonisValue::I32(flag)],
438            vec!["i"],
439            vec![],
440        )?;
441        Ok(())
442    }
443
444    /// Get whether data is saved.
445    ///
446    /// # Returns
447    /// True if data is being saved.
448    ///
449    /// # Errors
450    /// Returns `NanonisError` if communication fails.
451    pub fn hs_swp_save_data_get(&mut self) -> Result<bool, NanonisError> {
452        let result = self.quick_send("HSSwp.SaveDataGet", vec![], vec![], vec!["i"])?;
453
454        if !result.is_empty() {
455            Ok(result[0].as_i32()? != 0)
456        } else {
457            Err(NanonisError::Protocol("Invalid response".to_string()))
458        }
459    }
460
461    /// Set save options.
462    ///
463    /// # Arguments
464    /// * `options` - Save options configuration
465    ///
466    /// # Errors
467    /// Returns `NanonisError` if communication fails.
468    pub fn hs_swp_save_options_set(
469        &mut self,
470        options: &HSSwpSaveOptions,
471    ) -> Result<(), NanonisError> {
472        self.quick_send(
473            "HSSwp.SaveOptionsSet",
474            vec![
475                NanonisValue::String(options.comment.clone()),
476                NanonisValue::ArrayString(options.modules.clone()),
477            ],
478            vec!["+*c", "+*c"],
479            vec![],
480        )?;
481        Ok(())
482    }
483
484    /// Get save options.
485    ///
486    /// # Returns
487    /// An [`HSSwpSaveOptions`] struct with current options.
488    ///
489    /// # Errors
490    /// Returns `NanonisError` if communication fails.
491    pub fn hs_swp_save_options_get(&mut self) -> Result<HSSwpSaveOptions, NanonisError> {
492        let result = self.quick_send(
493            "HSSwp.SaveOptionsGet",
494            vec![],
495            vec![],
496            vec!["i", "*-c", "i", "i", "*+c"],
497        )?;
498
499        if result.len() >= 5 {
500            Ok(HSSwpSaveOptions {
501                comment: result[1].as_string()?.to_string(),
502                modules: result[4].as_string_array()?.to_vec(),
503            })
504        } else {
505            Err(NanonisError::Protocol("Invalid response".to_string()))
506        }
507    }
508
509    /// Start a high-speed sweep.
510    ///
511    /// # Arguments
512    /// * `wait_until_done` - Wait for sweep to complete before returning
513    /// * `timeout_ms` - Timeout in milliseconds (-1 for indefinite)
514    ///
515    /// # Errors
516    /// Returns `NanonisError` if communication fails.
517    pub fn hs_swp_start(
518        &mut self,
519        wait_until_done: bool,
520        timeout_ms: i32,
521    ) -> Result<(), NanonisError> {
522        let wait_flag = if wait_until_done { 1i32 } else { 0i32 };
523        self.quick_send(
524            "HSSwp.Start",
525            vec![NanonisValue::I32(wait_flag), NanonisValue::I32(timeout_ms)],
526            vec!["i", "i"],
527            vec![],
528        )?;
529        Ok(())
530    }
531
532    /// Stop the high-speed sweep.
533    ///
534    /// # Errors
535    /// Returns `NanonisError` if communication fails.
536    pub fn hs_swp_stop(&mut self) -> Result<(), NanonisError> {
537        self.quick_send("HSSwp.Stop", vec![], vec![], vec![])?;
538        Ok(())
539    }
540
541    /// Get the sweep status.
542    ///
543    /// # Returns
544    /// True if sweep is running.
545    ///
546    /// # Errors
547    /// Returns `NanonisError` if communication fails.
548    pub fn hs_swp_status_get(&mut self) -> Result<bool, NanonisError> {
549        let result = self.quick_send("HSSwp.StatusGet", vec![], vec![], vec!["I"])?;
550
551        if !result.is_empty() {
552            Ok(result[0].as_u32()? != 0)
553        } else {
554            Err(NanonisError::Protocol("Invalid response".to_string()))
555        }
556    }
557
558    /// Get the list of available sweep signals.
559    ///
560    /// # Returns
561    /// An [`HSSwpSignalList`] struct with signal information.
562    ///
563    /// # Errors
564    /// Returns `NanonisError` if communication fails.
565    pub fn hs_swp_swp_ch_sig_list_get(&mut self) -> Result<HSSwpSignalList, NanonisError> {
566        let result =
567            self.quick_send("HSSwp.SwpChSigListGet", vec![], vec![], vec!["+*c", "+*i"])?;
568
569        if result.len() >= 2 {
570            Ok(HSSwpSignalList {
571                names: result[0].as_string_array()?.to_vec(),
572                indices: result[1].as_i32_array()?.to_vec(),
573            })
574        } else {
575            Err(NanonisError::Protocol("Invalid response".to_string()))
576        }
577    }
578
579    /// Set the sweep channel signal.
580    ///
581    /// # Arguments
582    /// * `signal_index` - Index of sweep signal
583    /// * `timed_sweep` - Use timed sweep mode (ignores signal)
584    ///
585    /// # Errors
586    /// Returns `NanonisError` if communication fails.
587    pub fn hs_swp_swp_ch_signal_set(
588        &mut self,
589        signal_index: i32,
590        timed_sweep: bool,
591    ) -> Result<(), NanonisError> {
592        let timed_flag = if timed_sweep { 1i32 } else { 0i32 };
593        self.quick_send(
594            "HSSwp.SwpChSignalSet",
595            vec![
596                NanonisValue::I32(signal_index),
597                NanonisValue::I32(timed_flag),
598            ],
599            vec!["i", "i"],
600            vec![],
601        )?;
602        Ok(())
603    }
604
605    /// Get the sweep channel signal.
606    ///
607    /// # Returns
608    /// Tuple of (signal index, timed sweep enabled).
609    ///
610    /// # Errors
611    /// Returns `NanonisError` if communication fails.
612    pub fn hs_swp_swp_ch_signal_get(&mut self) -> Result<(i32, bool), NanonisError> {
613        let result = self.quick_send("HSSwp.SwpChSignalGet", vec![], vec![], vec!["i", "i"])?;
614
615        if result.len() >= 2 {
616            Ok((result[0].as_i32()?, result[1].as_i32()? != 0))
617        } else {
618            Err(NanonisError::Protocol("Invalid response".to_string()))
619        }
620    }
621
622    /// Set the sweep channel limits.
623    ///
624    /// # Arguments
625    /// * `limits` - Limits configuration
626    ///
627    /// # Errors
628    /// Returns `NanonisError` if communication fails.
629    pub fn hs_swp_swp_ch_limits_set(&mut self, limits: &HSSwpLimits) -> Result<(), NanonisError> {
630        let rel_flag = if limits.relative { 1i32 } else { 0i32 };
631        self.quick_send(
632            "HSSwp.SwpChLimitsSet",
633            vec![
634                NanonisValue::I32(rel_flag),
635                NanonisValue::F32(limits.start),
636                NanonisValue::F32(limits.stop),
637            ],
638            vec!["i", "f", "f"],
639            vec![],
640        )?;
641        Ok(())
642    }
643
644    /// Get the sweep channel limits.
645    ///
646    /// # Returns
647    /// An [`HSSwpLimits`] struct with current limits.
648    ///
649    /// # Errors
650    /// Returns `NanonisError` if communication fails.
651    pub fn hs_swp_swp_ch_limits_get(&mut self) -> Result<HSSwpLimits, NanonisError> {
652        let result =
653            self.quick_send("HSSwp.SwpChLimitsGet", vec![], vec![], vec!["i", "f", "f"])?;
654
655        if result.len() >= 3 {
656            Ok(HSSwpLimits {
657                relative: result[0].as_i32()? != 0,
658                start: result[1].as_f32()?,
659                stop: result[2].as_f32()?,
660            })
661        } else {
662            Err(NanonisError::Protocol("Invalid response".to_string()))
663        }
664    }
665
666    /// Set the number of sweep points.
667    ///
668    /// # Arguments
669    /// * `num_points` - Number of points in sweep
670    ///
671    /// # Errors
672    /// Returns `NanonisError` if communication fails.
673    pub fn hs_swp_swp_ch_num_pts_set(&mut self, num_points: u32) -> Result<(), NanonisError> {
674        self.quick_send(
675            "HSSwp.SwpChNumPtsSet",
676            vec![NanonisValue::U32(num_points)],
677            vec!["I"],
678            vec![],
679        )?;
680        Ok(())
681    }
682
683    /// Get the number of sweep points.
684    ///
685    /// # Returns
686    /// Number of points in sweep.
687    ///
688    /// # Errors
689    /// Returns `NanonisError` if communication fails.
690    pub fn hs_swp_swp_ch_num_pts_get(&mut self) -> Result<i32, NanonisError> {
691        let result = self.quick_send("HSSwp.SwpChNumPtsGet", vec![], vec![], vec!["i"])?;
692
693        if !result.is_empty() {
694            Ok(result[0].as_i32()?)
695        } else {
696            Err(NanonisError::Protocol("Invalid response".to_string()))
697        }
698    }
699
700    /// Set the sweep timing parameters.
701    ///
702    /// # Arguments
703    /// * `timing` - Timing configuration
704    ///
705    /// # Errors
706    /// Returns `NanonisError` if communication fails.
707    pub fn hs_swp_swp_ch_timing_set(&mut self, timing: &HSSwpTiming) -> Result<(), NanonisError> {
708        self.quick_send(
709            "HSSwp.SwpChTimingSet",
710            vec![
711                NanonisValue::F32(timing.initial_settling_s),
712                NanonisValue::F32(timing.settling_s),
713                NanonisValue::F32(timing.integration_s),
714                NanonisValue::F32(timing.max_slew_rate),
715            ],
716            vec!["f", "f", "f", "f"],
717            vec![],
718        )?;
719        Ok(())
720    }
721
722    /// Get the sweep timing parameters.
723    ///
724    /// # Returns
725    /// An [`HSSwpTiming`] struct with current timing.
726    ///
727    /// # Errors
728    /// Returns `NanonisError` if communication fails.
729    pub fn hs_swp_swp_ch_timing_get(&mut self) -> Result<HSSwpTiming, NanonisError> {
730        let result = self.quick_send(
731            "HSSwp.SwpChTimingGet",
732            vec![],
733            vec![],
734            vec!["f", "f", "f", "f"],
735        )?;
736
737        if result.len() >= 4 {
738            Ok(HSSwpTiming {
739                initial_settling_s: result[0].as_f32()?,
740                settling_s: result[1].as_f32()?,
741                integration_s: result[2].as_f32()?,
742                max_slew_rate: result[3].as_f32()?,
743            })
744        } else {
745            Err(NanonisError::Protocol("Invalid response".to_string()))
746        }
747    }
748
749    /// Set whether backward sweep is enabled.
750    ///
751    /// # Arguments
752    /// * `enabled` - Enable backward sweep
753    ///
754    /// # Errors
755    /// Returns `NanonisError` if communication fails.
756    pub fn hs_swp_swp_ch_bwd_sw_set(&mut self, enabled: bool) -> Result<(), NanonisError> {
757        let flag = if enabled { 1u32 } else { 0u32 };
758        self.quick_send(
759            "HSSwp.SwpChBwdSwSet",
760            vec![NanonisValue::U32(flag)],
761            vec!["I"],
762            vec![],
763        )?;
764        Ok(())
765    }
766
767    /// Get whether backward sweep is enabled.
768    ///
769    /// # Returns
770    /// True if backward sweep is enabled.
771    ///
772    /// # Errors
773    /// Returns `NanonisError` if communication fails.
774    pub fn hs_swp_swp_ch_bwd_sw_get(&mut self) -> Result<bool, NanonisError> {
775        let result = self.quick_send("HSSwp.SwpChBwdSwGet", vec![], vec![], vec!["I"])?;
776
777        if !result.is_empty() {
778            Ok(result[0].as_u32()? != 0)
779        } else {
780            Err(NanonisError::Protocol("Invalid response".to_string()))
781        }
782    }
783
784    /// Set the backward sweep delay.
785    ///
786    /// # Arguments
787    /// * `delay_s` - Delay between forward and backward sweep in seconds
788    ///
789    /// # Errors
790    /// Returns `NanonisError` if communication fails.
791    pub fn hs_swp_swp_ch_bwd_delay_set(&mut self, delay_s: f32) -> Result<(), NanonisError> {
792        self.quick_send(
793            "HSSwp.SwpChBwdDelaySet",
794            vec![NanonisValue::F32(delay_s)],
795            vec!["f"],
796            vec![],
797        )?;
798        Ok(())
799    }
800
801    /// Get the backward sweep delay.
802    ///
803    /// # Returns
804    /// Delay in seconds.
805    ///
806    /// # Errors
807    /// Returns `NanonisError` if communication fails.
808    pub fn hs_swp_swp_ch_bwd_delay_get(&mut self) -> Result<f32, NanonisError> {
809        let result = self.quick_send("HSSwp.SwpChBwdDelayGet", vec![], vec![], vec!["f"])?;
810
811        if !result.is_empty() {
812            Ok(result[0].as_f32()?)
813        } else {
814            Err(NanonisError::Protocol("Invalid response".to_string()))
815        }
816    }
817
818    /// Set the Z-controller behavior during sweep.
819    ///
820    /// # Arguments
821    /// * `config` - Z-controller configuration
822    ///
823    /// # Errors
824    /// Returns `NanonisError` if communication fails.
825    pub fn hs_swp_z_ctrl_off_set(&mut self, config: &HSSwpZCtrl) -> Result<(), NanonisError> {
826        let switch_off = if config.switch_off { 0i32 } else { 1i32 }; // 0=switch off, 1=don't switch
827        self.quick_send(
828            "HSSwp.ZCtrlOffSet",
829            vec![
830                NanonisValue::I32(switch_off),
831                NanonisValue::I32(config.controller_index),
832                NanonisValue::F32(config.averaging_time_s),
833                NanonisValue::F32(config.z_offset_m),
834                NanonisValue::F32(config.control_time_s),
835            ],
836            vec!["i", "i", "f", "f", "f"],
837            vec![],
838        )?;
839        Ok(())
840    }
841
842    /// Get the Z-controller behavior during sweep.
843    ///
844    /// # Returns
845    /// An [`HSSwpZCtrl`] struct with current configuration.
846    ///
847    /// # Errors
848    /// Returns `NanonisError` if communication fails.
849    pub fn hs_swp_z_ctrl_off_get(&mut self) -> Result<HSSwpZCtrl, NanonisError> {
850        let result = self.quick_send(
851            "HSSwp.ZCtrlOffGet",
852            vec![],
853            vec![],
854            vec!["i", "i", "f", "f", "f"],
855        )?;
856
857        if result.len() >= 5 {
858            Ok(HSSwpZCtrl {
859                switch_off: result[0].as_i32()? == 0, // 0=switch off, 1=don't switch
860                controller_index: result[1].as_i32()?,
861                averaging_time_s: result[2].as_f32()?,
862                z_offset_m: result[3].as_f32()?,
863                control_time_s: result[4].as_f32()?,
864            })
865        } else {
866            Err(NanonisError::Protocol("Invalid response".to_string()))
867        }
868    }
869}