Skip to main content

rusty_tip/
types.rs

1use serde::{Deserialize, Serialize};
2
3// Re-export nanonis-rs types from their respective submodules
4pub use nanonis_rs::motor::{
5    Amplitude, Frequency, MotorAxis, MotorDirection, MotorDisplacement, MotorGroup,
6    MotorMovement, MovementMode, Position3D, StepCount,
7};
8pub use nanonis_rs::oscilloscope::{
9    OsciData, OsciTriggerMode, OscilloscopeIndex, OversamplingIndex, SampleCount, SignalStats,
10    TimebaseIndex, TriggerConfig, TriggerLevel, TriggerMode, TriggerSlope,
11};
12pub use nanonis_rs::Position;
13pub use nanonis_rs::bias::PulseMode;
14pub use nanonis_rs::scan::{ScanAction, ScanConfig, ScanDirection, ScanFrame};
15pub use nanonis_rs::signals::SignalFrame;
16pub use nanonis_rs::tcplog::{TCPLogStatus, TCPLoggerData};
17pub use nanonis_rs::z_ctrl::ZControllerHold;
18// DataToGet is extended locally with Stable variant
19
20use std::time::{Duration, Instant};
21
22/// Simple tip shape - matches original controller
23#[derive(Debug, Clone, Copy, PartialEq, Serialize, Deserialize, Default)]
24pub enum TipShape {
25    #[default]
26    Blunt,
27    Sharp,
28    Stable,
29}
30
31/// Session metadata for signal tracking
32#[derive(Debug, Clone)]
33pub struct SessionMetadata {
34    pub session_id: String,
35    pub signal_names: Vec<String>,   // All signal names
36    pub active_indices: Vec<usize>,  // Which signals are being monitored
37    pub primary_signal_index: usize, // Index of the primary signal
38    pub session_start: f64,          // Session start timestamp
39}
40
41/// Extended DataToGet with application-specific Stable variant
42#[derive(Debug, Clone, Copy, PartialEq)]
43pub enum DataToGet {
44    Current,
45    NextTrigger,
46    Wait2Triggers,
47    Stable { readings: u32, timeout: Duration },
48}
49
50/// Timestamped version of SignalFrame for efficient buffering
51#[derive(Debug, Clone)]
52pub struct TimestampedSignalFrame {
53    /// The lightweight signal frame
54    pub signal_frame: SignalFrame,
55    /// High-resolution timestamp when frame was received
56    pub timestamp: Instant,
57    /// Time relative to collection start
58    pub relative_time: Duration,
59}
60
61impl TimestampedSignalFrame {
62    /// Create a new timestamped signal frame from lightweight signal frame
63    /// Just adds high-resolution timestamp to existing SignalFrame
64    pub fn new(signal_frame: SignalFrame, start_time: Instant) -> Self {
65        let timestamp = Instant::now();
66        Self {
67            signal_frame,
68            timestamp,
69            relative_time: timestamp.duration_since(start_time),
70        }
71    }
72}
73
74// ==================== Experiment Data with Lightweight Frames ====================
75
76/// Complete experiment result containing action outcome and synchronized signal data
77/// Now uses lightweight SignalFrame structures for better memory efficiency
78#[derive(Debug)]
79pub struct ExperimentData {
80    /// Result of the executed action
81    pub action_result: crate::actions::ActionResult,
82    /// Lightweight signal frames (much more memory efficient)
83    pub signal_frames: Vec<TimestampedSignalFrame>,
84    /// TCP logger configuration for context (stored once, not per frame)
85    pub tcp_config: crate::action_driver::TCPReaderConfig,
86    /// When the action started
87    pub action_start: Instant,
88    /// When the action ended  
89    pub action_end: Instant,
90    /// Total action duration
91    pub total_duration: Duration,
92}
93
94/// Experiment data for action chain execution with timing for each action
95#[derive(Debug)]
96pub struct ChainExperimentData {
97    /// Results of each action in the chain
98    pub action_results: Vec<crate::actions::ActionResult>,
99    /// All signal frames collected during the entire chain execution
100    pub signal_frames: Vec<TimestampedSignalFrame>,
101    /// TCP logger configuration for context
102    pub tcp_config: crate::action_driver::TCPReaderConfig,
103    /// Start and end times for each action in the chain
104    pub action_timings: Vec<(Instant, Instant)>,
105    /// When the entire chain started
106    pub chain_start: Instant,
107    /// When the entire chain ended
108    pub chain_end: Instant,
109    /// Duration of entire chain execution
110    pub total_duration: Duration,
111}
112
113impl ExperimentData {
114    /// Get signal data captured during action execution
115    pub fn data_during_action(&self) -> Vec<&TimestampedSignalFrame> {
116        self.signal_frames
117            .iter()
118            .filter(|frame| {
119                frame.timestamp >= self.action_start && frame.timestamp <= self.action_end
120            })
121            .collect()
122    }
123
124    /// Get signal data before action execution
125    pub fn data_before_action(&self, duration: Duration) -> Vec<&TimestampedSignalFrame> {
126        let cutoff = self.action_start - duration;
127        self.signal_frames
128            .iter()
129            .filter(|frame| frame.timestamp >= cutoff && frame.timestamp < self.action_start)
130            .collect()
131    }
132
133    /// Get signal data after action execution
134    pub fn data_after_action(&self, duration: Duration) -> Vec<&TimestampedSignalFrame> {
135        let cutoff = self.action_end + duration;
136        self.signal_frames
137            .iter()
138            .filter(|frame| frame.timestamp > self.action_end && frame.timestamp <= cutoff)
139            .collect()
140    }
141
142    /// Get full TCPLoggerData for compatibility when needed
143    /// This reconstructs the full data structures using stored TCP config
144    pub fn get_tcp_logger_data(&self) -> Vec<TCPLoggerData> {
145        self.signal_frames
146            .iter()
147            .map(|frame| TCPLoggerData {
148                num_channels: self.tcp_config.channels.len() as u32,
149                oversampling: self.tcp_config.oversampling as f32,
150                counter: frame.signal_frame.counter,
151                state: TCPLogStatus::Running,
152                data: frame.signal_frame.data.clone(),
153            })
154            .collect()
155    }
156}
157
158impl ChainExperimentData {
159    /// Get signal data captured during a specific action in the chain
160    ///
161    /// # Arguments
162    /// * `action_index` - Index of the action in the chain (0-based)
163    ///
164    /// # Returns
165    /// Vector of signal frames collected during the specified action
166    pub fn data_during_action(&self, action_index: usize) -> Vec<&TimestampedSignalFrame> {
167        if let Some((start, end)) = self.action_timings.get(action_index) {
168            self.signal_frames
169                .iter()
170                .filter(|frame| frame.timestamp >= *start && frame.timestamp <= *end)
171                .collect()
172        } else {
173            Vec::new()
174        }
175    }
176
177    /// Get signal data captured between two actions in the chain
178    ///
179    /// # Arguments
180    /// * `action1_index` - Index of first action (end time)
181    /// * `action2_index` - Index of second action (start time)
182    ///
183    /// # Returns
184    /// Vector of signal frames collected between the two specified actions
185    pub fn data_between_actions(
186        &self,
187        action1_index: usize,
188        action2_index: usize,
189    ) -> Vec<&TimestampedSignalFrame> {
190        if let (Some((_, end1)), Some((start2, _))) = (
191            self.action_timings.get(action1_index),
192            self.action_timings.get(action2_index),
193        ) {
194            self.signal_frames
195                .iter()
196                .filter(|frame| frame.timestamp > *end1 && frame.timestamp < *start2)
197                .collect()
198        } else {
199            Vec::new()
200        }
201    }
202
203    /// Get signal data before the chain execution
204    ///
205    /// # Arguments
206    /// * `duration` - How far back from chain start to collect data
207    ///
208    /// # Returns
209    /// Vector of signal frames from before the chain started
210    pub fn data_before_chain(&self, duration: Duration) -> Vec<&TimestampedSignalFrame> {
211        let cutoff = self.chain_start - duration;
212        self.signal_frames
213            .iter()
214            .filter(|frame| frame.timestamp >= cutoff && frame.timestamp < self.chain_start)
215            .collect()
216    }
217
218    /// Get signal data after the chain execution
219    ///
220    /// # Arguments
221    /// * `duration` - How far forward from chain end to collect data
222    ///
223    /// # Returns
224    /// Vector of signal frames from after the chain ended
225    pub fn data_after_chain(&self, duration: Duration) -> Vec<&TimestampedSignalFrame> {
226        let cutoff = self.chain_end + duration;
227        self.signal_frames
228            .iter()
229            .filter(|frame| frame.timestamp > self.chain_end && frame.timestamp <= cutoff)
230            .collect()
231    }
232
233    /// Get all signal data for the entire chain execution
234    ///
235    /// # Returns
236    /// Vector of signal frames from chain start to chain end
237    pub fn data_for_entire_chain(&self) -> Vec<&TimestampedSignalFrame> {
238        self.signal_frames
239            .iter()
240            .filter(|frame| {
241                frame.timestamp >= self.chain_start && frame.timestamp <= self.chain_end
242            })
243            .collect()
244    }
245
246    /// Get timing information for a specific action
247    ///
248    /// # Arguments
249    /// * `action_index` - Index of the action in the chain
250    ///
251    /// # Returns
252    /// Optional tuple of (start_time, end_time, duration)
253    pub fn action_timing(&self, action_index: usize) -> Option<(Instant, Instant, Duration)> {
254        self.action_timings
255            .get(action_index)
256            .map(|(start, end)| (*start, *end, end.duration_since(*start)))
257    }
258
259    /// Get summary statistics for the chain execution
260    ///
261    /// # Returns
262    /// Tuple of (total_actions, successful_actions, total_frames, chain_duration)
263    pub fn chain_summary(&self) -> (usize, usize, usize, Duration) {
264        let total_actions = self.action_results.len();
265        let successful_actions = self
266            .action_results
267            .iter()
268            .filter(|result| matches!(result, crate::actions::ActionResult::Success))
269            .count();
270        let total_frames = self.signal_frames.len();
271
272        (
273            total_actions,
274            successful_actions,
275            total_frames,
276            self.total_duration,
277        )
278    }
279}
280
281/// Result of an auto-approach operation
282#[derive(Debug, Clone, PartialEq, Eq)]
283pub enum AutoApproachResult {
284    /// Auto-approach completed successfully
285    Success,
286    /// Auto-approach timed out before completion
287    Timeout,
288    /// Auto-approach failed (e.g., hardware error, abnormal termination)
289    Failed(String),
290    /// Auto-approach was already running when attempted to start
291    AlreadyRunning,
292    /// Auto-approach was cancelled/stopped externally
293    Cancelled,
294}
295
296impl AutoApproachResult {
297    /// Check if the result represents a successful operation
298    pub fn is_success(&self) -> bool {
299        matches!(self, AutoApproachResult::Success)
300    }
301
302    /// Check if the result represents a failure
303    pub fn is_failure(&self) -> bool {
304        matches!(
305            self,
306            AutoApproachResult::Failed(_) | AutoApproachResult::Timeout
307        )
308    }
309
310    /// Get error description if this is a failure
311    pub fn error_message(&self) -> Option<&str> {
312        match self {
313            AutoApproachResult::Failed(msg) => Some(msg),
314            AutoApproachResult::Timeout => Some("Auto-approach timed out"),
315            AutoApproachResult::AlreadyRunning => Some("Auto-approach already running"),
316            AutoApproachResult::Cancelled => Some("Auto-approach was cancelled"),
317            AutoApproachResult::Success => None,
318        }
319    }
320}
321
322/// Status information for auto-approach operations
323#[derive(Debug, Clone, PartialEq, Eq)]
324pub enum AutoApproachStatus {
325    /// Auto-approach is not running
326    Idle,
327    /// Auto-approach is currently running
328    Running,
329    /// Auto-approach status is unknown (e.g., communication error)
330    Unknown,
331}
332