nanonis_rs/client/scan/mod.rs
1mod types;
2pub use types::*;
3
4use super::NanonisClient;
5use crate::error::NanonisError;
6use crate::types::{NanonisValue, Position};
7use std::time::Duration;
8
9impl NanonisClient {
10 /// Start, stop, pause or resume a scan
11 pub fn scan_action(
12 &mut self,
13 scan_action: ScanAction,
14 scan_direction: ScanDirection,
15 ) -> Result<(), NanonisError> {
16 self.quick_send(
17 "Scan.Action",
18 vec![
19 NanonisValue::U16(scan_action.into()),
20 NanonisValue::U32(scan_direction.into()),
21 ],
22 vec!["H", "I"],
23 vec![],
24 )?;
25 Ok(())
26 }
27
28 /// Configure the scan frame parameters
29 pub fn scan_frame_set(&mut self, frame: ScanFrame) -> Result<(), NanonisError> {
30 self.quick_send(
31 "Scan.FrameSet",
32 vec![
33 NanonisValue::F32(frame.center.x as f32),
34 NanonisValue::F32(frame.center.y as f32),
35 NanonisValue::F32(frame.width_m),
36 NanonisValue::F32(frame.height_m),
37 NanonisValue::F32(frame.angle_deg),
38 ],
39 vec!["f", "f", "f", "f", "f"],
40 vec![],
41 )?;
42 Ok(())
43 }
44
45 /// Get the scan frame parameters
46 pub fn scan_frame_get(&mut self) -> Result<ScanFrame, NanonisError> {
47 let result = self.quick_send(
48 "Scan.FrameGet",
49 vec![],
50 vec![],
51 vec!["f", "f", "f", "f", "f"],
52 )?;
53 if result.len() >= 5 {
54 let center_x = result[0].as_f64()?;
55 let center_y = result[1].as_f64()?;
56 let width = result[2].as_f32()?;
57 let height = result[3].as_f32()?;
58 let angle = result[4].as_f32()?;
59
60 Ok(ScanFrame::new(
61 Position::new(center_x, center_y),
62 width,
63 height,
64 angle,
65 ))
66 } else {
67 Err(NanonisError::Protocol(
68 "Invalid scan frame response".to_string(),
69 ))
70 }
71 }
72
73 /// Get the scan buffer parameters
74 /// Returns: (channel_indexes, pixels, lines)
75 pub fn scan_buffer_get(&mut self) -> Result<(Vec<i32>, i32, i32), NanonisError> {
76 let result =
77 self.quick_send("Scan.BufferGet", vec![], vec![], vec!["i", "*i", "i", "i"])?;
78 if result.len() >= 4 {
79 let channel_indexes = result[1].as_i32_array()?.to_vec();
80 let pixels = result[2].as_i32()?;
81 let lines = result[3].as_i32()?;
82 Ok((channel_indexes, pixels, lines))
83 } else {
84 Err(NanonisError::Protocol(
85 "Invalid scan buffer response".to_string(),
86 ))
87 }
88 }
89
90 /// Get the current scan status.
91 ///
92 /// Returns whether a scan is currently running or not.
93 ///
94 /// # Returns
95 /// `true` if scan is running, `false` if scan is not running.
96 ///
97 /// # Errors
98 /// Returns `NanonisError` if communication fails or protocol error occurs.
99 ///
100 /// # Examples
101 /// ```no_run
102 /// use nanonis_rs::NanonisClient;
103 ///
104 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
105 ///
106 /// if client.scan_status_get()? {
107 /// println!("Scan is currently running");
108 /// } else {
109 /// println!("Scan is stopped");
110 /// }
111 /// # Ok::<(), Box<dyn std::error::Error>>(())
112 /// ```
113 pub fn scan_status_get(&mut self) -> Result<bool, NanonisError> {
114 let result = self.quick_send("Scan.StatusGet", vec![], vec![], vec!["I"])?;
115
116 match result.first() {
117 Some(value) => Ok(value.as_u32()? == 1),
118 None => Err(NanonisError::Protocol(
119 "No scan status returned".to_string(),
120 )),
121 }
122 }
123
124 /// Configure the scan buffer parameters.
125 ///
126 /// Sets which channels to record during scanning and the scan resolution.
127 /// The channel indexes refer to the 24 signals assigned in the Signals Manager (0-23).
128 ///
129 /// **Important**: The number of pixels is coerced to the closest multiple of 16
130 /// because scan data is sent in packages of 16 pixels.
131 ///
132 /// # Arguments
133 /// * `channel_indexes` - Indexes of channels to record (0-23 for signals in Signals Manager)
134 /// * `pixels` - Number of pixels per line (coerced to multiple of 16)
135 /// * `lines` - Number of scan lines
136 ///
137 /// # Errors
138 /// Returns `NanonisError` if communication fails or invalid parameters provided.
139 ///
140 /// # Examples
141 /// ```no_run
142 /// use nanonis_rs::NanonisClient;
143 ///
144 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
145 ///
146 /// // Record channels 0, 1, and 2 with 512x512 resolution
147 /// client.scan_buffer_set(vec![0, 1, 2], 512, 512)?;
148 ///
149 /// // High resolution scan with multiple channels
150 /// client.scan_buffer_set(vec![0, 1, 2, 3, 4], 1024, 1024)?;
151 /// # Ok::<(), Box<dyn std::error::Error>>(())
152 /// ```
153 pub fn scan_buffer_set(
154 &mut self,
155 channel_indexes: Vec<i32>,
156 pixels: i32,
157 lines: i32,
158 ) -> Result<(), NanonisError> {
159 self.quick_send(
160 "Scan.BufferSet",
161 vec![
162 NanonisValue::ArrayI32(channel_indexes),
163 NanonisValue::I32(pixels),
164 NanonisValue::I32(lines),
165 ],
166 vec!["+*i", "i", "i"],
167 vec![],
168 )?;
169 Ok(())
170 }
171
172 /// Configure scan speed parameters.
173 ///
174 /// Sets the tip scanning speeds for both forward and backward scan directions.
175 /// You can specify either linear speed or time per line, and set speed ratios
176 /// between forward and backward scanning.
177 ///
178 /// # Arguments
179 /// * `forward_linear_speed_m_s` - Forward linear speed in m/s
180 /// * `backward_linear_speed_m_s` - Backward linear speed in m/s
181 /// * `forward_time_per_line_s` - Forward time per line in seconds
182 /// * `backward_time_per_line_s` - Backward time per line in seconds
183 /// * `keep_parameter_constant` - Which parameter to keep constant: 0=no change, 1=linear speed, 2=time per line
184 /// * `speed_ratio` - Backward tip speed relative to forward speed
185 ///
186 /// # Errors
187 /// Returns `NanonisError` if communication fails or invalid parameters provided.
188 ///
189 /// # Examples
190 /// ```no_run
191 /// use nanonis_rs::NanonisClient;
192 /// use nanonis_rs::scan::ScanConfig;
193 ///
194 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
195 ///
196 /// // Set 1 μm/s forward, 2 μm/s backward, keep linear speed constant
197 /// let config = ScanConfig {
198 /// forward_linear_speed_m_s: 1e-6,
199 /// backward_linear_speed_m_s: 2e-6,
200 /// forward_time_per_line_s: 0.1,
201 /// backward_time_per_line_s: 0.05,
202 /// keep_parameter_constant: 1,
203 /// speed_ratio: 2.0,
204 /// };
205 /// client.scan_config_set(config)?;
206 /// # Ok::<(), Box<dyn std::error::Error>>(())
207 /// ```
208 pub fn scan_config_set(&mut self, config: ScanConfig) -> Result<(), NanonisError> {
209 self.quick_send(
210 "Scan.SpeedSet",
211 vec![
212 NanonisValue::F32(config.forward_linear_speed_m_s),
213 NanonisValue::F32(config.backward_linear_speed_m_s),
214 NanonisValue::F32(config.forward_time_per_line_s),
215 NanonisValue::F32(config.backward_time_per_line_s),
216 NanonisValue::U16(config.keep_parameter_constant),
217 NanonisValue::F32(config.speed_ratio),
218 ],
219 vec!["f", "f", "f", "f", "H", "f"],
220 vec![],
221 )?;
222 Ok(())
223 }
224
225 /// Get the current scan speed parameters.
226 ///
227 /// Returns all scan speed configuration values including linear speeds,
228 /// time per line, and speed ratio settings.
229 ///
230 /// # Returns
231 /// A tuple containing:
232 /// - `f32` - Forward linear speed (m/s)
233 /// - `f32` - Backward linear speed (m/s)
234 /// - `f32` - Forward time per line (s)
235 /// - `f32` - Backward time per line (s)
236 /// - `u16` - Keep parameter constant (0=linear speed, 1=time per line)
237 /// - `f32` - Speed ratio (backward relative to forward)
238 ///
239 /// # Errors
240 /// Returns `NanonisError` if communication fails or protocol error occurs.
241 ///
242 /// # Examples
243 /// ```no_run
244 /// use nanonis_rs::NanonisClient;
245 ///
246 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
247 ///
248 /// let config = client.scan_speed_get()?;
249 ///
250 /// println!("Forward speed: {:.2e} m/s", config.forward_linear_speed_m_s);
251 /// println!("Backward speed: {:.2e} m/s", config.backward_linear_speed_m_s);
252 /// println!("Speed ratio: {:.1}", config.speed_ratio);
253 /// # Ok::<(), Box<dyn std::error::Error>>(())
254 /// ```
255 pub fn scan_speed_get(&mut self) -> Result<ScanConfig, NanonisError> {
256 let result = self.quick_send(
257 "Scan.SpeedGet",
258 vec![],
259 vec![],
260 vec!["f", "f", "f", "f", "H", "f"],
261 )?;
262
263 if result.len() >= 6 {
264 Ok(ScanConfig {
265 forward_linear_speed_m_s: result[0].as_f32()?,
266 backward_linear_speed_m_s: result[1].as_f32()?,
267 forward_time_per_line_s: result[2].as_f32()?,
268 backward_time_per_line_s: result[3].as_f32()?,
269 keep_parameter_constant: result[4].as_u16()?,
270 speed_ratio: result[5].as_f32()?,
271 })
272 } else {
273 Err(NanonisError::Protocol(
274 "Invalid scan speed response".to_string(),
275 ))
276 }
277 }
278
279 /// Get the current XY position during scanning.
280 ///
281 /// Returns the current values of the X and Y signals, useful for monitoring
282 /// tip position during scanning operations.
283 ///
284 /// # Arguments
285 /// * `wait_newest_data` - If `true`, discards first value and waits for fresh data
286 ///
287 /// # Returns
288 /// A tuple containing (X position in m, Y position in m)
289 ///
290 /// # Errors
291 /// Returns `NanonisError` if communication fails or protocol error occurs.
292 ///
293 /// # Examples
294 /// ```no_run
295 /// use nanonis_rs::NanonisClient;
296 ///
297 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
298 ///
299 /// // Get current position immediately
300 /// let (x, y) = client.scan_xy_pos_get(false)?;
301 /// println!("Current position: ({:.6}, {:.6}) m", x, y);
302 ///
303 /// // Wait for fresh position data
304 /// let (x, y) = client.scan_xy_pos_get(true)?;
305 /// # Ok::<(), Box<dyn std::error::Error>>(())
306 /// ```
307 pub fn scan_xy_pos_get(&mut self, wait_newest_data: bool) -> Result<(f32, f32), NanonisError> {
308 let wait_flag = if wait_newest_data { 1u32 } else { 0u32 };
309
310 let result = self.quick_send(
311 "Scan.XYPosGet",
312 vec![NanonisValue::U32(wait_flag)],
313 vec!["I"],
314 vec!["f", "f"],
315 )?;
316
317 if result.len() >= 2 {
318 Ok((result[0].as_f32()?, result[1].as_f32()?))
319 } else {
320 Err(NanonisError::Protocol(
321 "Invalid XY position response".to_string(),
322 ))
323 }
324 }
325
326 /// Save the current scan data buffer to file.
327 ///
328 /// Saves the current scan data into a file. If `wait_until_saved` is true,
329 /// the function waits for the save operation to complete before returning.
330 ///
331 /// # Arguments
332 /// * `wait_until_saved` - If `true`, waits for save completion before returning
333 /// * `timeout_ms` - Timeout in milliseconds (-1 for indefinite wait)
334 ///
335 /// # Returns
336 /// `true` if timeout occurred while waiting for save completion, `false` otherwise
337 ///
338 /// # Errors
339 /// Returns `NanonisError` if communication fails or protocol error occurs.
340 ///
341 /// # Examples
342 /// ```no_run
343 /// use nanonis_rs::NanonisClient;
344 ///
345 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
346 ///
347 /// // Save immediately without waiting
348 /// let timed_out = client.scan_save(false, 5000)?;
349 ///
350 /// // Save and wait up to 30 seconds for completion
351 /// let timed_out = client.scan_save(true, 30000)?;
352 /// if timed_out {
353 /// println!("Save operation timed out");
354 /// }
355 /// # Ok::<(), Box<dyn std::error::Error>>(())
356 /// ```
357 pub fn scan_save(
358 &mut self,
359 wait_until_saved: bool,
360 timeout_ms: i32,
361 ) -> Result<bool, NanonisError> {
362 let wait_flag = if wait_until_saved { 1u32 } else { 0u32 };
363
364 let result = self.quick_send(
365 "Scan.Save",
366 vec![NanonisValue::U32(wait_flag), NanonisValue::I32(timeout_ms)],
367 vec!["I", "i"],
368 vec!["I"],
369 )?;
370
371 match result.first() {
372 Some(value) => Ok(value.as_u32()? == 1),
373 None => Err(NanonisError::Protocol(
374 "No save status returned".to_string(),
375 )),
376 }
377 }
378
379 /// Get scan frame data for a specific channel and direction.
380 ///
381 /// Returns the complete 2D scan data array for the selected channel.
382 /// The channel must be one of the channels configured in the scan buffer.
383 ///
384 /// # Arguments
385 /// * `channel_index` - Index of channel to retrieve data from (must be in acquired channels)
386 /// * `data_direction` - Data direction: `true` for forward, `false` for backward
387 ///
388 /// # Returns
389 /// A tuple containing:
390 /// - `String` - Channel name
391 /// - `Vec<Vec<f32>>` - 2D scan data array \[rows\]\[columns\]
392 /// - `bool` - Scan direction: `true` for up, `false` for down
393 ///
394 /// # Errors
395 /// Returns `NanonisError` if:
396 /// - Invalid channel index (not in acquired channels)
397 /// - Communication fails or protocol error occurs
398 ///
399 /// # Examples
400 /// ```no_run
401 /// use nanonis_rs::NanonisClient;
402 ///
403 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
404 ///
405 /// // Get forward scan data for channel 0
406 /// let (channel_name, data, scan_up) = client.scan_frame_data_grab(0, true)?;
407 /// println!("Channel: {}, Direction: {}", channel_name, if scan_up { "up" } else { "down" });
408 /// println!("Data size: {}x{}", data.len(), data[0].len());
409 ///
410 /// // Get backward scan data
411 /// let (_, back_data, _) = client.scan_frame_data_grab(0, false)?;
412 /// # Ok::<(), Box<dyn std::error::Error>>(())
413 /// ```
414 pub fn scan_frame_data_grab(
415 &mut self,
416 channel_index: u32,
417 data_direction: bool,
418 ) -> Result<(String, Vec<Vec<f32>>, bool), NanonisError> {
419 let direction_flag = if data_direction { 1u32 } else { 0u32 };
420
421 let result = self.quick_send(
422 "Scan.FrameDataGrab",
423 vec![
424 NanonisValue::U32(channel_index),
425 NanonisValue::U32(direction_flag),
426 ],
427 vec!["I", "I"],
428 vec!["i", "*-c", "i", "i", "2f", "I"],
429 )?;
430
431 if result.len() >= 6 {
432 let channel_name = result[1].as_string()?.to_string();
433 let rows = result[2].as_i32()? as usize;
434 let cols = result[3].as_i32()? as usize;
435
436 // Parse 2D array from flat f32 array
437 let flat_data = result[4].as_f32_array()?;
438 let mut data_2d = Vec::with_capacity(rows);
439
440 for row in 0..rows {
441 let start_idx = row * cols;
442 let end_idx = start_idx + cols;
443 data_2d.push(flat_data[start_idx..end_idx].to_vec());
444 }
445
446 let scan_direction = result[5].as_u32()? == 1;
447 Ok((channel_name, data_2d, scan_direction))
448 } else {
449 Err(NanonisError::Protocol(
450 "Invalid frame data response".to_string(),
451 ))
452 }
453 }
454
455 /// Wait for the End-of-Scan.
456 ///
457 /// Waits for the current scan to complete or timeout to occur, whichever comes first.
458 /// This is useful for synchronizing operations with scan completion.
459 ///
460 /// # Arguments
461 /// * `timeout` - Timeout duration (-1 for indefinite wait)
462 ///
463 /// # Returns
464 /// A tuple containing:
465 /// - `bool` - `true` if timeout occurred, `false` if scan completed normally
466 /// - `String` - File path where data was auto-saved (empty if no auto-save)
467 ///
468 /// # Errors
469 /// Returns `NanonisError` if communication fails or protocol error occurs.
470 ///
471 /// # Examples
472 /// ```no_run
473 /// use nanonis_rs::NanonisClient;
474 /// use nanonis_rs::scan::{ScanAction, ScanDirection};
475 /// use std::time::Duration;
476 ///
477 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
478 ///
479 /// // Start a scan
480 /// client.scan_action(ScanAction::Start, ScanDirection::Up)?;
481 ///
482 /// // Wait for scan to complete (up to 5 minutes)
483 /// let (timed_out, file_path) = client.scan_wait_end_of_scan(Duration::from_secs(300))?;
484 ///
485 /// if timed_out {
486 /// println!("Scan timed out after 5 minutes");
487 /// } else {
488 /// println!("Scan completed");
489 /// if !file_path.is_empty() {
490 /// println!("Data saved to: {}", file_path);
491 /// }
492 /// }
493 /// # Ok::<(), Box<dyn std::error::Error>>(())
494 /// ```
495 pub fn scan_wait_end_of_scan(
496 &mut self,
497 timeout: Duration,
498 ) -> Result<(bool, String), NanonisError> {
499 let result = self.quick_send(
500 "Scan.WaitEndOfScan",
501 vec![NanonisValue::I32(timeout.as_millis() as i32)],
502 vec!["i"],
503 vec!["I", "I", "*-c"],
504 )?;
505
506 if result.len() >= 3 {
507 let timeout_occurred = result[0].as_u32()? == 1;
508 let file_path = result[2].as_string()?.to_string();
509 Ok((timeout_occurred, file_path))
510 } else {
511 Err(NanonisError::Protocol(
512 "Invalid scan wait response".to_string(),
513 ))
514 }
515 }
516
517 /// Get scan properties configuration.
518 ///
519 /// Returns current scan properties including continuous scan, bouncy scan,
520 /// autosave, series name, comment, modules names, and autopaste settings.
521 ///
522 /// # Returns
523 /// `ScanProps` structure containing all scan property settings
524 ///
525 /// # Errors
526 /// Returns `NanonisError` if communication fails or protocol error occurs.
527 ///
528 /// # Examples
529 /// ```no_run
530 /// use nanonis_rs::NanonisClient;
531 ///
532 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
533 ///
534 /// let props = client.scan_props_get()?;
535 /// println!("Continuous scan: {:?}", props.continuous_scan);
536 /// println!("Bouncy scan: {:?}", props.bouncy_scan);
537 /// println!("Series name: {}", props.series_name);
538 /// # Ok::<(), Box<dyn std::error::Error>>(())
539 /// ```
540 pub fn scan_props_get(&mut self) -> Result<ScanProps, NanonisError> {
541 let result = self.quick_send(
542 "Scan.PropsGet",
543 vec![],
544 vec![],
545 vec!["I", "I", "I", "i", "*-c", "i", "*-c", "i", "i", "*+c", "i", "*+i", "i", "i", "*+c", "I"],
546 )?;
547
548 if result.len() >= 16 {
549 // Index 0: Continuous scan (0=Off, 1=On)
550 let continuous_scan = result[0].as_u32()? == 1;
551
552 // Index 1: Bouncy scan (0=Off, 1=On)
553 let bouncy_scan = result[1].as_u32()? == 1;
554
555 // Index 2: Autosave (0=All, 1=Next, 2=Off)
556 let autosave = AutosaveMode::try_from(result[2].as_u32()?)?;
557
558 // Index 3: series_name size (i)
559 // Index 4: series_name string (*-c)
560 let series_name = result[4].as_string()?.to_string();
561
562 // Index 5: comment size (i)
563 // Index 6: comment string (*-c)
564 let comment = result[6].as_string()?.to_string();
565
566 // Index 7: modules_names size (i)
567 // Index 8: modules_names count (i)
568 // Index 9: modules_names array (*+c)
569 let modules_names = result[9].as_string_array()?.to_vec();
570
571 // Index 10: num_params_per_module array size (i)
572 // Index 11: num_params_per_module array (*+i)
573 let num_params_per_module = result[11].as_i32_array()?.to_vec();
574
575 // Index 12: parameters rows (i)
576 let rows = result[12].as_i32()?;
577 // Index 13: parameters columns (i)
578 let cols = result[13].as_i32()?;
579 // Index 14: parameters 2D array (*+c)
580 let params_flat = result[14].as_string_array()?;
581
582 // Convert flat array to 2D (rows x cols)
583 let mut parameters = Vec::new();
584 for row in 0..rows as usize {
585 let mut row_params = Vec::new();
586 for col in 0..cols as usize {
587 let idx = row * (cols as usize) + col;
588 if idx < params_flat.len() {
589 row_params.push(params_flat[idx].clone());
590 }
591 }
592 parameters.push(row_params);
593 }
594
595 // Index 15: Autopaste (0=All, 1=Next, 2=Off)
596 let autopaste = AutopasteMode::try_from(result[15].as_u32()?)?;
597
598 Ok(ScanProps {
599 continuous_scan,
600 bouncy_scan,
601 autosave,
602 series_name,
603 comment,
604 modules_names,
605 num_params_per_module,
606 parameters,
607 autopaste,
608 })
609 } else {
610 Err(NanonisError::Protocol(format!(
611 "Invalid scan props response: expected 16 values, got {}",
612 result.len()
613 )))
614 }
615 }
616
617 /// Set scan properties configuration.
618 ///
619 /// Configures scan parameters including continuous scan, bouncy scan,
620 /// autosave behavior, series name, comment, and autopaste settings.
621 /// Use `ScanPropsBuilder` to set only the properties you want to change.
622 ///
623 /// # Arguments
624 /// * `builder` - Builder with properties to set. Fields set to `None` will not be changed.
625 ///
626 /// # Errors
627 /// Returns `NanonisError` if communication fails or invalid parameters provided.
628 ///
629 /// # Examples
630 /// ```no_run
631 /// use nanonis_rs::NanonisClient;
632 /// use nanonis_rs::scan::{ScanPropsBuilder, AutosaveMode, AutopasteMode};
633 ///
634 /// let mut client = NanonisClient::new("127.0.0.1", 6501)?;
635 ///
636 /// // Set only specific properties using the builder
637 /// let builder = ScanPropsBuilder::new()
638 /// .continuous_scan(true) // Enable continuous scan
639 /// .bouncy_scan(true) // Enable bouncy scan
640 /// .autosave(AutosaveMode::Off); // Disable autosave
641 ///
642 /// client.scan_props_set(builder)?;
643 /// # Ok::<(), Box<dyn std::error::Error>>(())
644 /// ```
645 pub fn scan_props_set(&mut self, builder: ScanPropsBuilder) -> Result<(), NanonisError> {
646 // Convert boolean options to u32 (0=no change, 1=On, 2=Off)
647 let continuous_flag = match builder.continuous_scan {
648 None => 0u32,
649 Some(true) => 1u32,
650 Some(false) => 2u32,
651 };
652
653 let bouncy_flag = match builder.bouncy_scan {
654 None => 0u32,
655 Some(true) => 1u32,
656 Some(false) => 2u32,
657 };
658
659 let autosave_flag = match builder.autosave {
660 None => 0u32,
661 Some(mode) => mode.into(),
662 };
663
664 let autopaste_flag = match builder.autopaste {
665 None => 0u32,
666 Some(mode) => mode.into(),
667 };
668
669 // For strings/arrays: empty string/array means no change
670 let series_name = builder.series_name.unwrap_or_default();
671 let comment = builder.comment.unwrap_or_default();
672 let modules_names = builder.modules_names.unwrap_or_default();
673
674 self.quick_send(
675 "Scan.PropsSet",
676 vec![
677 NanonisValue::U32(continuous_flag),
678 NanonisValue::U32(bouncy_flag),
679 NanonisValue::U32(autosave_flag),
680 NanonisValue::String(series_name),
681 NanonisValue::String(comment),
682 NanonisValue::ArrayString(modules_names),
683 NanonisValue::U32(autopaste_flag),
684 ],
685 vec!["I", "I", "I", "+*c", "+*c", "+*c", "I"],
686 vec![],
687 )?;
688
689 Ok(())
690 }
691
692 /// Paste background image at specified location.
693 ///
694 /// Pastes a previously copied background image to the scan buffer at the
695 /// specified position. This is used for background subtraction operations.
696 ///
697 /// # Arguments
698 /// * `x` - X position in meters for paste location
699 /// * `y` - Y position in meters for paste location
700 ///
701 /// # Errors
702 /// Returns `NanonisError` if communication fails or no background is available.
703 pub fn scan_background_paste(&mut self, x: f64, y: f64) -> Result<(), NanonisError> {
704 self.quick_send(
705 "Scan.BackgroundPaste",
706 vec![NanonisValue::F64(x), NanonisValue::F64(y)],
707 vec!["d", "d"],
708 vec![],
709 )?;
710 Ok(())
711 }
712
713 /// Delete the stored background image.
714 ///
715 /// Removes the background image from memory, freeing resources and
716 /// disabling background subtraction until a new background is captured.
717 ///
718 /// # Errors
719 /// Returns `NanonisError` if communication fails.
720 pub fn scan_background_delete(&mut self) -> Result<(), NanonisError> {
721 self.quick_send("Scan.BackgroundDelete", vec![], vec![], vec![])?;
722 Ok(())
723 }
724}