pub struct BufferedTCPReader { /* private fields */ }Expand description
Buffered TCP reader that continuously collects timestamped signal data
This component creates a background thread that reads lightweight SignalFrame data from TCPLoggerStream’s channel and buffers it with high-resolution timestamps in a circular buffer. It provides time-windowed query methods for retrieving data before, during, and after specific time periods.
§High-Frequency Performance (2kHz+)
IMPORTANT: At sampling rates above 1kHz, lock contention becomes critical:
- Current implementation uses
Mutex<VecDeque>suitable for <1kHz - For 2kHz+, consider
crossbeam::queue::ArrayQueue(lock-free) - Alternative:
parking_lot::RwLockfor multiple concurrent readers - Query methods must complete in <0.1ms to avoid data loss
§Memory Efficiency
Works with lightweight SignalFrame structures (just counter + data) throughout the entire pipeline, avoiding the overhead of full TCPLoggerData per frame.
§Architecture
- TCPLoggerStream converts protocol data to SignalFrame (protocol → lightweight conversion)
- BufferedTCPReader adds timestamps to SignalFrame (timing layer)
- Thread-safe time-windowed queries while continuous collection runs in background
Implementations§
Source§impl BufferedTCPReader
impl BufferedTCPReader
Sourcepub fn new(
host: &str,
port: u16,
buffer_size: usize,
num_channels: u32,
oversampling: f32,
) -> Result<Self, NanonisError>
pub fn new( host: &str, port: u16, buffer_size: usize, num_channels: u32, oversampling: f32, ) -> Result<Self, NanonisError>
Create a new BufferedTCPReader with automatic background data collection
This establishes a connection to the TCP logger stream and starts a background thread for continuous data buffering with lightweight SignalFrame structures.
§Arguments
host- TCP server host address (e.g., “127.0.0.1”)port- TCP logger data stream port (typically 6590)buffer_size- Maximum number of frames to keep in circular buffernum_channels- Number of channels being recorded by TCP loggeroversampling- Oversampling rate configured for TCP logger
§Returns
A BufferedTCPReader with active background collection, ready for queries
§Implementation Notes
- Creates TCPLoggerStream and gets its background reader channel
- Starts buffering thread that converts SignalFrame to TimestampedSignalFrame
- Implements circular buffer behavior (drops oldest when full)
Sourcepub fn is_buffering(&self) -> bool
pub fn is_buffering(&self) -> bool
Check if the background buffering thread is still active
§Returns
true if buffering is active, false if stopped or failed
Sourcepub fn buffer_utilization(&self) -> f64
pub fn buffer_utilization(&self) -> f64
Sourcepub fn uptime(&self) -> Duration
pub fn uptime(&self) -> Duration
Get the total uptime of the buffered TCP reader
Returns the duration since the BufferedTCPReader was created and started collecting data. This can be useful for monitoring, logging, and understanding the data collection timespan.
§Returns
Duration since the reader was started
§Thread Safety
This method is very fast as it only reads the start_time field and calculates the current duration. No locks are acquired.
§Example
let tcp_reader = BufferedTCPReader::new("127.0.0.1", 6590, 1000, 24, 100.0)?;
// Later...
let uptime = tcp_reader.uptime();
println!("TCP reader has been running for {:.1}s", uptime.as_secs_f64());
// Useful for rate calculations
let (frame_count, _, _) = tcp_reader.buffer_stats();
let avg_rate = frame_count as f64 / uptime.as_secs_f64();
println!("Average data rate: {:.1} frames/sec", avg_rate);Sourcepub fn get_data_since(&self, since: Instant) -> Vec<TimestampedSignalFrame>
pub fn get_data_since(&self, since: Instant) -> Vec<TimestampedSignalFrame>
Get all signal data since a specific timestamp
§Arguments
since- Timestamp to start collecting data from
§Returns
Vector of timestamped signal frames from the specified time onwards
§Thread Safety
This method acquires a lock on the buffer briefly to copy matching frames. Lock is held for minimal time to avoid blocking the buffering thread.
Sourcepub fn get_data_between(
&self,
start: Instant,
end: Instant,
) -> Vec<TimestampedSignalFrame>
pub fn get_data_between( &self, start: Instant, end: Instant, ) -> Vec<TimestampedSignalFrame>
Get signal data between two timestamps (time window query)
§Arguments
start- Start of time window (inclusive)end- End of time window (inclusive)
§Returns
Vector of timestamped signal frames within the specified time window
§Thread Safety
Minimizes lock time to avoid blocking the buffering thread.
§Usage
This is the core method for synchronized data collection during actions. Typically used to get data before/during/after specific operations.
Sourcepub fn get_recent_data(&self, duration: Duration) -> Vec<TimestampedSignalFrame>
pub fn get_recent_data(&self, duration: Duration) -> Vec<TimestampedSignalFrame>
Get recent signal data for a specific duration
§Arguments
duration- How far back to collect data from current time
§Returns
Vector of timestamped signal frames from the recent past
§Thread Safety
Delegates to get_data_since() which minimizes lock time.
§Usage
Convenient for real-time monitoring and getting recent signal history without needing to track specific timestamps
Sourcepub fn get_all_data(&self) -> Vec<TimestampedSignalFrame>
pub fn get_all_data(&self) -> Vec<TimestampedSignalFrame>
Get all buffered signal data
§Returns
Vector containing all currently buffered timestamped signal frames
§Thread Safety
WARNING: This clones the entire buffer. For large buffers, prefer time-windowed queries. Lock is held briefly but cloning large amounts of data may still impact performance.
§Usage
Useful for final data collection when stopping buffering, or for full experiment analysis
Sourcepub fn get_tcp_config(&self) -> (u32, f32)
pub fn get_tcp_config(&self) -> (u32, f32)
Sourcepub fn buffer_stats(&self) -> (usize, usize, Duration)
pub fn buffer_stats(&self) -> (usize, usize, Duration)
Sourcepub fn get_recent_frames(&self, count: usize) -> Vec<TimestampedSignalFrame>
pub fn get_recent_frames(&self, count: usize) -> Vec<TimestampedSignalFrame>
Get the most recent N frames from the buffer
Returns frames in reverse chronological order (newest first).
If fewer than count frames are available, returns all available frames.
§Arguments
count- Maximum number of frames to retrieve
§Returns
Vector of timestamped signal frames, newest first
§Example
let recent_100 = tcp_reader.get_recent_frames(100);Sourcepub fn get_oldest_frames(&self, count: usize) -> Vec<TimestampedSignalFrame>
pub fn get_oldest_frames(&self, count: usize) -> Vec<TimestampedSignalFrame>
Get the oldest N frames from the buffer
Returns frames in chronological order (oldest first).
If fewer than count frames are available, returns all available frames.
Useful for FIFO processing or getting a stable baseline.
§Arguments
count- Maximum number of frames to retrieve
§Returns
Vector of timestamped signal frames, oldest first
§Example
let baseline = tcp_reader.get_oldest_frames(50);Sourcepub fn frame_count(&self) -> usize
pub fn frame_count(&self) -> usize
Get the current number of frames in the buffer
Returns the total count of frames currently stored in the circular buffer. This can be used to check buffer fill level or validate requests.
§Returns
Number of frames currently buffered
§Example
let available = tcp_reader.frame_count();
if available >= 100 {
let data = tcp_reader.get_recent_frames(100);
}Sourcepub fn get_frame_range(
&self,
start_idx: usize,
count: usize,
) -> Vec<TimestampedSignalFrame>
pub fn get_frame_range( &self, start_idx: usize, count: usize, ) -> Vec<TimestampedSignalFrame>
Get frames from a specific range in the buffer
Returns frames starting from start_idx (0 = oldest frame) for count frames.
If the range extends beyond available data, returns available frames only.
Useful for windowed analysis or specific time periods.
§Arguments
start_idx- Starting index (0 = oldest frame in buffer)count- Number of frames to retrieve from start_idx
§Returns
Vector of timestamped signal frames in chronological order
§Example
// Get frames 50-149 (middle section of buffer)
let middle_data = tcp_reader.get_frame_range(50, 100);Sourcepub fn has_frames(&self, min_count: usize) -> bool
pub fn has_frames(&self, min_count: usize) -> bool
Check if the buffer has at least N frames available
Convenience method to check data availability before requesting frames. More efficient than getting frame_count() when you only need a threshold check.
§Arguments
min_count- Minimum number of frames required
§Returns
True if buffer contains at least min_count frames
§Example
if tcp_reader.has_frames(100) {
let stable_data = tcp_reader.get_recent_frames(100);
} else {
println!("Not enough data yet, only {} frames", tcp_reader.frame_count());
}Sourcepub fn clear_buffer(&self)
pub fn clear_buffer(&self)
Clear all buffered data
This removes all frames from the buffer, effectively resetting it to an empty state. The background thread continues to run and will start filling the buffer again. This is useful when you want to discard old data and start fresh.
§Example
// Clear any stale data before starting a new measurement
tcp_reader.clear_buffer();
thread::sleep(Duration::from_millis(500)); // Wait for fresh data
let fresh_data = tcp_reader.get_recent_data(Duration::from_millis(100));