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